home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-07-31 | 127.9 KB | 3,452 lines |
-
- The Oberon Guide
-
- System Release 1.2
-
- Jurg Gutknecht
-
-
- Abstract
-
- This guide provides a concise and detailed description of the Oberon system
- on three different levels: the user's level, the level of programmers of
- tools, and the level of implementors of new viewer classes. In particular,
- the guide features a complete documentation of standard commands, a commented
- series of important interface definitions, and a tutorial collection of Oberon
- programs exemplifying the typical Oberon programming style.
-
-
- Table of Contents
-
- Abstract
-
- Introduction
- History
- Design Principles
- Acknowledgement
-
- User's Guide
- Commands and Tools
- The Edit Tool Package
- The Draw Tool Package
- The Paint Tool Package
- The System Tool Package
- The Compiler Tool Package
- The Miscellaneous Tool Package
- The Backup Tool Package
- The Net Server Tool Package
- The ColorSystem Tool Package
-
- Guide for Programmers of Commands
- Oberon's module hierarchy
- The Display System
- The Text System
- The Oberon Core
- Tutorial Examples
- Write time stamp to system log
- Process selected text
- Open viewer in system track, generate, and display text data
-
- Open viewer in user track and display existing text
- Grow text viewer
- Process viewer text or sequence of texts, depending on context
-
- Delete selected part of text in marked viewer
- Copy most recently selected text part to caret's position
- Copy font from visibly marked position to text selection
- Move caret to next character written in italics
-
- Guide for Programmers of new Frame Classes and Viewer Types
- Frames as Active Objects
- Standard Menu Viewers
- The Canonical Decomposition of an Application
- Tutorial Examples 1: Frame oriented operations
- Display text line within text frame
- Track caret
- Tutorial Examples 2: Text oriented operations
- Save text in buffer
- Insert contents of buffer in text
-
- Literature
- Ceres Workstation
- Oberon Language
- Oberon System
-
- List of Oberon Error Numbers
- Incorrect use of language Oberon-2
- Limitations of implementation
- Run-time Trap Numbers
-
-
- Introduction
-
-
- History
-
- Oberon is simultaneously the name of a project and of its outcome. The
- project was started by Niklaus Wirth and the author late in 1985 with
- the goal of developing a modern and portable operating system for
- personal workstations. Its results are an implementation of the system
- for the Ceres computer and a programming language (see section Literature).
-
- The development of the language Oberon needs perhaps a short justification.
- It became quite inevitable because the type-system of available languages
- turned out to be too restrictive to express the desired data model in a
- natural and safe way. We refer to report for a definition of the new language,
- and to the third and fourth chapter of this text for some examples of its
- application.
-
-
- Design Principles
-
- For the present, we focus on the system Oberon, beginning with a brief
- overview of its design principles. The underlying dynamic model is extremely
- simple. There exists a single process acting as a common carrier of multiple
- tasks. This process repetitively interprets commands , which are the official
- entities of execution in Oberon. Commands are atomic actions operating on the
- global state of the system. Unlike customary interactive programs, they
- rigorously avoid direct dialogs with the system user.
-
- The following typical examples indicate the bandwidth covered by the concept
- of command: Placing the caret, inserting a character into a text, selecting
- a piece of text, deleting a selected piece of text, applying a new font to a
- piece of text, searching a pattern in a text, compiling a software module,
- opening a viewer, backing up a sequence of files to diskette, and displaying
- a directory. We emphasize that the execution of a command always results in
- non-volatile information. For example, in the last example, this means that
- the displayed directory is a text that might immediately undergo further
- processing. Typically, commands report the outcome of their execution in
- the form of an entry in the system-log . Therefore, the log provides a
- complete protocol of the current session.
-
- Commands are initiated by input actions. Apart from a few universal operations,
- every input action is connected with a displayed viewer , to which its further
- handling is delegated. A viewer in Oberon is a rectangular area on the screen
- that can display any kind of data. Most viewers feature a thin frame and a
- title-bar containing a menu. Any mouse-oriented input is handled by the viewer
- the mouse points to. Data from the keyboard is immediately passed over to the
- current so-called focus-viewer . We notice that command interpretation is a
- highly decentralized activity in Oberon and, as such, is a substantial
- contribution to what we consider as Oberon's most important quality, namely
- unlimited extensibility.
-
- Implementing a new viewer type is a very powerful but also quite far-reaching
- method to extend the Oberon system. It is only appropriate in conjunction with
- the installation of a new class of displayable objects (for example graphics
- or tables). The fourth chapter will provide more insight into this topic.
-
- A more moderate way to increase the system's functionality consists in
- adding new commands operating on objects of an already existing class
- (for example a language compiler operating on text). We shall see in
- the third chapter that Oberon's open and coherent modular architecture
- provides effective support for that. Practically all system ingredients
- and resources are directly accessible and usable via modular interfaces
- on as high a level of abstraction as possible.
-
- We should deduce from the foregoing that there is no symbolic wall in
- Oberon separating actual users from developers. Users are encouraged
- to customize the system and tailor it to their individual needs by
- designing and implementing private commands and facilities. Little is
- "hardwired" in the system. However, there are several general conventions
- and existing tools. They are presented in the next chapter.
-
-
- Acknowledgement
-
- I gratefully acknowledge Niklaus Wirth's initiative and willingness for
- travelling through the adventures of designing and building a new workstation,
- a new language, and a new operating system, escpecially under the given severe
- restrictions of personal resources. Without his competence and extraordinary
- commitment this mammoth-project could not have been successfully completed.
- I am also very grateful for the possibility to include an extract of Niklaus
- Wirth's Draw guide in the first chapter. My thanks further go to the Oberon
- user's community, in particular to Martin Reiser, Hans-Peter Mossenbock, and
- Beverly Sanders for their constructive critisism and valuable suggestions for
- improvements.
-
-
- User's Guide
-
-
- Commands and Tools
-
- Among the classes of possible objects to be handled by a computer system the
- class of texts plays a key role. Not only are input and output data frequently
- represented as text, but also objects and commands are often identified by
- their name. Text is, therefore, a predefined class of objects in Oberon.
-
- This manifests itself immediately after system startup, when, besides of the
- log-viewer , a so-called tool viewer is automatically opened on the screen.
- It contains a list of command names, some of them followed by parameters.
- Command names in Oberon are of the form M.P, where M designates a module
- (package) and P a procedure (operation) that is provided by the module.
- A user activates a command simply by pointing to its name with the mouse
- and clicking the middle mouse key. For example, activating the command Edit.
- Open will result in a new viewer showing some default text. More often than
- not, the execution of a command is parameterized. For example, the opening of
- another tool needs the specification of its name, as in System.Open Edit.Tool
- or in System.Open Net.Tool. Although typical, this is not by far the most
- general case of a parameter specification. Some commands accept an entire
- list of names following the command name and execute repeatedly for each
- member of the list. In principle, a text obeying an arbitrary syntax
- (understood by the command) could be passed over equally well. Commands
- may even expect as parameters objects of any kind currently existing in the
- system such as viewers, text selections, caret, and a global star-shaped
- pointer. Some commands even allow different ways of paramewter specification.
- For example, if System.Open or Edit.Open is called with a "^"-symbol instead
- of a file name following the command name, then the file name is taken from
- the most recent selection. In general, a "^"-symbol following a command name
- always refers to the current selection.
-
- It is noteworthy that tools are ordinary texts distinguishing themselves from
- more usual texts only by their structure and contents. In particular, tools
- are amenable to editing operations. Looking at this differently, we recognize
- that commands like Edit.Open Explanations.Text may well slip into a prose text
- and be activated directly in situ. Obviously, no limits are set to fantasy
- exploiting this universal scheme of command interpretation.
-
- One rather moderate application of the universal scheme discussed above is the
- construction of interconnected texts. As a matter of fact, the set of standard
- tools is structured as a tree with the System.Tool as ancestor and the tools
- listed in the System.Tool as its descendants. We recall that the hierarchical
- tool system may easily be customized on the fly by adjusting command lists
- (including parameters) to personal requirements, reconfiguring the tool
- hierarchy, installing new tools, or even providing on-line documentation.
-
- We now discuss editing operations. Recall first that most commands are
- interpreted individually by viewers. There are, however, a few more universal
- operations, which are handled directly by the central system. For example,
- when you type escape, all marks on the display are removed, including caret
- and text selections. Or, when you type ctrl shift del the system immediately
- terminates the execution of the current command and opens a trap-viewer
- displaying the state of the interrupted process. Notice that we have just
- identified you as the reader of this guide with a user of the system. In
- order to simplify phrasing, we shall henceforth occasionally do so tacitly.
-
- Next we turn to viewers and display-specific operations. You can put your
- primary display screen into any one of three different modes by hitting one
- of the function keys PF1, PF2, and PF3. PF1 specifies white script on a black
- background, PF2 turns the display off, and PF3 specifies black script on a
- white background. Oberon uses a tiling viewer system. The display is divided
- into vertical tracks, and each track is further subdivided into viewers. In
- reality, the structure of viewers is three-dimensional. A new track may in
- fact overlay one or, more generally, an integral number of existing tracks.
- The original configuration will be reestablished when the overlaying track
- is later removed.
-
- Although the global screen layout can be changed, we relate our current
- explanations to the standard layout showing two tracks, a larger user track
- on the left and a narrower system track on the right. In principle, viewers
- are allocated automatically by the respective commands using a little
- heuristics. For example, tool viewers are opened in the system track, and
- document viewers in the user track. However, you can override any automatic
- allocation by first placing the pointer at the location where you desire the
- top of the new viewer to be placed. The pointer is a star-shaped marker, and
- it is placed by moving the mouse to the desired location and then hitting the
- SETUP key. In order to change the size of an existing viewer, simply point to
- its title-bar, press the left mouse key, and move the mouse up or down
- accordingly. You can also conveniently move a viewer to any different place on
- the display screen by starting exactly as just explained, then interclicking
- the middle mouse-key, dragging the mouse to the new location, and releasing
- all keys there.
-
- Interclicking means clicking (pressing and releasing) a secondary mouse button
- at an arbitrary time while a primary mouse key is being held down. In general,
- interclicking is an efficient and versatile tool to multiply the expressiveness
- of the mouse. In Oberon, interclicking is applied according to a systematic
- underlying pattern. You will find out more about this pattern in the following
- chapters. Perhaps the easiest and most important rule says that the current
- command is nullified, if all remaining mouse-keys have been interclicked
- (not necessarily simultaneously) during the action.
-
- By convention, most viewers (so-called menu-viewers) show a header consisting
- of a title and a list of selected commands (menu). These commands automatically
- refer to their own viewer. In the case of ordinary text viewers, commands are
- included from the System tool-package and the Edit tool-package. System.Close
- removes the viewer, Edit.Copy opens a new viewer displaying the same instance
- of text, Edit.Grow lets the viewer grow to the size of a full track or of the
- whole display, Edit.Locate locates a text position, and Edit.Store stores the
- text on file.We shall explain these commands in greater detail in the following
- sections on tools.
-
- First, we fix some terminology and general conventions. We shall call marked
- an object or a location if it is visibly or invisibly marked by the earlier
- introduced star-shaped pointer. Visibility of the pointer is irrelevant in
- most cases. As an exception we mention the explicit allocation of a viewer,
- which requests the pointer to be visible. Note that an explicitly allocated
- viewer is automatically marked. Also note that the pointer is initially
- invisible and placed in the lower left corner of the display.
-
- By convention, the title of a viewer is normally either the name of the
- displayed object or the name of the command that opened this viewer. Further,
- if a list of names is passed to a command as a parameter, it must be terminated
- by a symbol other than a name, for example by the character "~". By another
- convention, the upper right corner of the display is reserved for the log
- viewer reporting on the progress and result in the execution of a command.
- Finally, there are several default extensions of file names. Among them are
- Text, Graph and Pict for file copies of texts, graphics, and bitmap pictures
- respectively, and Scn.Fnt for screen font files.
-
- In the following sections we shall use the terms parameter and parameter
- list in the restricted sense of "item following the command name" and "list
- of items following the command name" respectively.
-
-
- The Edit Tool Package
-
- We have stated earlier that extensibility was a key objective in the design
- of Oberon. It was therefore enticing to realize also system-oriented commands
- as extensions of the system-core on a highest possible level in the modular
- hierarchy, thereby achieving maximal flexibility. Such a strategy is
- particularly appropriate for text editing. It manifests itself in the
- existence of an edit tool package providing an extensible set of powerful
- editing commands. Nevertheless, several built-in commands are interpreted
- directly by text frames. They include positionning the text within its viewer,
- placing the caret, inserting a typed character, selecting a part of text,
- deleting a selected part of text, copying a selected part of text, copying
- attributes and, most importantly, executing an arbitrary command which is
- specified by its name. We should point out that all of these built-in commands
- are applicable in particular to menu-bars (which, in fact, are ordinary text
- frames featuring an inverted background color).
-
-
- Mouse Commands
-
- Text positionning. In order to reposition a text within a viewer, move the
- mouse into the viewer's scrolling-zone. This is a vertical bar along the
- left borderline of about 0.5 cm width. You can scroll forward by pressing
- the left mouse key, moving the mouse, and releasing the key when the text
- line that you want to become the top line is underlined. Notice that every
- text viewer shows a small crossbeam indicating the current position of the
- displayed section within the entire text. You can position a text directly
- by clicking the middle mouse key at the location where you want the
- crossbeam to be. If you wish the beginning of the text to be displayed, you
- can alternatively simply click the right mouse key anywhere within the
- scroll-bar.
-
- Placing the caret. If you want to place the caret, move the mouse to the
- desired text, press the left mouse button and, while keeping it down, move
- the caret to the desired position. Any subsequently typed characters are
- then inserted at this position.
-
- As a side remark notice that German Umlaute are entered by pressing the
- following combinations of keys: ctrl a for ae, ctrl shift a for Ae, ctrl o
- for oe, ctrl shift o for Oe, ctrl u for ue, and ctrl shift U for Ue.
-
- A O U (a umlaut represented by ae, etc.)
- - a o u
- shift A O U
- ctrl ae oe ue
- shift ctrl Ae Oe Ue
-
- Selecting text. You can select any contiguous stretch of text by moving the
- mouse to the desired beginning, pressing the right mouse button and, while
- holding it down, dragging the selection to the end. If you click twice at
- the beginning, the selection is automatically extended to the origin of
- that text line. If a piece of text is too large to be selectable within
- a single viewer, use Edit.Copy to open an adjacent second viewer. Then
- select the beginning of the stretch of text in the upper viewer and its
- end in the lower viewer separately. Note that a separate selection may
- be active for each displayed text section, including headers of viewers.
- If several selections exist simultaneously on the display, commands
- normally refer to the most recent one or to the most recent ones.
-
- There are the following interesting interclick-variants of caret placing and
- text selection that combine these marking operations effectively with text
- editing. Remember the general rule saying that any mouse-controlled operation
- that is currently under execution can be nullified by interclicking all
- remaining mouse-keys.
-
- Copying text. If you interclick the middle mouse button while you are placing
- the caret, the most recent selection is automatically copied to the caret's
- position as soon as you release the left button. This feature is particularly
- convenient for copying a specific template to several different places.
- Alternatively, if the caret is already set and you click the middle mouse key
- while you are selecting a piece of text, the selected text is copied to the
- caret's position when you release the select-button. This option is most
- conveniently used in order to copy a given string to various places. In order
- to neutralize (undo) any "interclick", simply click the remaining third button
- while still holding down the primary key.
-
- Copying attributes. If you interclick the right mouse button while you are
- placing the caret, the character attributes (font, color, vertical offset) of
- the character by the caret is automatically applied to the most recent
- selection as soon as you release the left button.
-
- Deleting text. If you click the left mouse-button while selecting a text, the
- selected text is eventually deleted. Here also, the interclick can be undone
- by an additional interclick of the middle mouse-key.
-
- Notice that the above described editing operations are applicable to a header
- of a viewer only restrictedly.A header cannot be changed. Nor can the caret be
- placed in a header. Further notice that the copy variant and the delete variant
- of the select command apply also in the case of large selections involving
- split viewers.
-
- Activating a command. Activating a named command from within a text viewer is
- generic and therefore the most general built-in operation. In order to do it
- simply point to the command's name and click the middle mouse key. Sometimes
- (e.g. in a test phase) it is important that the newest version of the module
- providing the desired command is loaded before the command is actually
- executed. In order to force this, simply interclick the left key while you
- are pressing the middle mouse key and pointing to the command's name.
-
- If command execution fails, the system falls into a trap. There is no
- interactive debugger currently available under Oberon. However, a trap handler
- is automatically called whenever a trap has occurred. It displays the state of
- the interrupted process, including the stack of procedure activations. It also
- relates the location of trap to a position in the source program text. In order
- to find the failing statement simply display the source program and mark its
- viewer. Then select the position number in the trap viewer and invoke
- Edit.Locate.
-
- The following table summarizes the basic meanings of the three mouse-keys:
- The left key is the point-key. It is used to focus a certain location,
- i.e. to place the caret. The middle key is the execute-key. Pressing and
- releasing it causes the appropriate command interpreter to be called. The
- right key is the select-key. It is used to select objects within a viewer.
-
- Summary of mouse commands
-
- primary button left middle right
- ---------------------------------------------------------------
- secondary
- button
-
- none set caret execute select
-
- left - free and delete
- execute
-
- middle copy selection - copy this
- selection
-
- right copy attributes - -
-
-
- Text viewers display text in a standard line-oriented way. In particular,
- they do not support any non-trivial formatting, such as automatic line-
- breaking or right-justifying paragraphs, for example. Font variation, color
- specification, and vertical offset, are however possible. We recommend the
- following fonts to be used in connection with text viewers: Syntax10.Scn.Fnt
- (default font), Syntax10i.Scn.Fnt (italics variant), Syntax10b.Scn.Fnt
- (bold face variant), and Courier8.Scn.Fnt (non-proportional font).
-
- According to Oberon's basic scheme, additional functionality is provided by
- the text edit tool package. It contains the following commands.
-
-
- Edit Commands
-
- Edit.Open
- opens a viewer in the user track displaying the specified text. The text is
- alternatively specified by a parameter on the command line or, if a "^"-symbol
- follows the command name, by the most recent selection of a name. If none
- exists, a default name is taken. In order to override automatic allocation,
- place the pointer anywhere on the screen.
-
- Edit.Show M.X
- opens a viewer in the user track displaying the specified object X of module
- M. If the implementation of M is available, the implementation of X is shown,
- otherwise X 's definition is displayed.
-
- Edit.Store
- writes the text in the marked viewer to the file with the name defined by the
- parameter, or, if called from the menu line of a text viewer, writes the
- displayed text to the file with the name of the viewer.
-
- Edit.Recall
- inserts the most recently deleted piece of text at the position of the caret.
-
- Edit.CopyFont
- transfers the font from the marked location to the most recent text selection.
-
- Edit.ChangeFont
- applies the font specified by the parameter to the most recent text selection.
-
- Edit.ChangeColor
- applies the color specified by the parameter to the most recent text selection.
-
- Edit.ChangeOffset
- applies the vertical offset specified by the parameter to the most recent text
- selection.
-
- Edit.Search
- searches a pattern in the marked text. The pattern is defined by the most
- recent text selection. If none exists, the previous pattern is used. Searching
- is started at the position of the caret. If none exists in the marked text,
- searching starts at the beginning. The initial value of the pattern is the
- space character.
-
- Edit.Locate
- positions the text in the marked viewer according to the position-number
- indicated by the most recent text selection. Leading non-numerical items
- in the text selection are ignored.
-
-
- Edit.Print ServerName ["%"] { (TextFileName | "*") ["/" NofCopies] }
- sends all texts specified by the parameter list to the print server whose name
- is taken from the first entry in the parameter list. Names in the parameter
- list refer to text files, the symbol * to the text in the marked viewer. The
- symbol % specifies the vanilla-print option. If active, the texts are printed
- in a single monospaced small Font (Gacha10l). This option is typically used
- for printing source program listings. NofCopies optionally specifies the
- desired number of copies. It must be a single-digit number. This command
- assumes that the correct user identification has previously been installed
- (by calling System.SetUser ).
-
-
- The Draw Tool Package
-
- The system called Draw serves to prepare line drawings. They contain lines,
- text captions, and other items, and are displayed in graphic viewers (more
- precisely: in menu viewers' graphic frames). A graphic viewer shows an
- excerpt of the drawing plane, and several viewers may show different parts
- of a drawing.
-
- The most frequently used commands are built-in as mouse clicks and combinations
- of clicks. Additional commands are selectable from texts, either in viewer's
- menus (title bar) or in the text called Draw.Tool. The mouse buttons have the
- following principal functions;
-
- left : draw
- middle : move/copy
- right : select
-
- A mouse command is identified (1) by the button k0 pressed initially, (2) by
- the initial position P0 of the cursor, (3) by the set of keys k1 of buttons
- pressed until the last is released, and (4) the cursor position P1 at the
- time of release.
-
- The basic system consists of the modules Draw, GraphicFrames, and Graphics.
- These modules contain the facilities to generate and handle horizontal and
- vertical lines, text captions, and macros. Additional modules serve to
- introduce other elements, such as rectangles and circles, and the system is
- extensible, i.e. further modules may be introduced to handle further items.
-
-
- Basic Commands
-
- The command Draw.Open (available in the Draw.Tool text) opens a new viewer
- and displays the graph with the name given as parameter. If no parameter is
- specified with the command, the last text selection is taken as parameter.
- We suggest that file names use the extension Graph.
-
- Drawing a line. In order to draw a horizontal or vertical line from P0 to P1,
- press the button with the cursor at P0 and, while holding the button, move
- the mouse and cursor to P1. Then release the button. If P0 and P1 differ in
- both their x and y coordinates, the end point is adjusted so that the line
- is either horizontal or vertical.
-
- Writing a caption. First place the cursor where the caption is to appear. Then
- click the left button, causing a crosshair to appear. This is called the caret.
- Then type the text. Only single line texts are accepted. The DEL key may be
- used to retract characters (backspace).
-
- Selecting. Most commands require the specification of operands, and many
- implicitly assume the previously selected elements - the selection - to be
- their operands. A single element is selected by pointing at it with the cursor
- and then clicking the right mouse button. This also causes previously seleted
- elements to be deselected. If their selection is to be retained, also click
- the left button. This action is called an interclick. If you wish to select
- several elements at once, move the cursor from P0 to P1 while holding the
- right key. Then all elements lying within the rectangle with diagonally
- opposite corners at P0 and P1 are selected.
-
- Selected lines are displayed as dotted lines, selected captions (and macros)
- by inverse video mode. A mocro is selected by pointing at its lower left corner.
-
- Moving. If you wish to move (displace) a set of elements, first select them,
- and then move the cursor from P0 to P1 while holding the middle button. The
- vector from P0 to P1 specifies the movement and is called the displacement
- vector. P0 and P1 may lie in different viewers displaying the same graph.
-
- Copying. Similarly, the selected elements may be copied (duplicated). In
- addition to pressing the middle button while indicating the displacement
- vector, interclick the left button.
-
- The copy command may also be used to copy elements from one graph into another
- graph by moving the cursor from one viewer into another viewer displaying the
- destination graph.
-
- A text caption may be copied from a text frame into a graphic frame and
- vice-versa. There exist two ways to achieve this:
-
- 1. First place the caret at the destination position, then select the text
- and interclick the middle button.
-
- 2. First select the text to be copied, then place the caret at the destination
- point by clicking the left and interclicking the middle button.
-
- Shifting the plane. You may shift the entire drawing plane behind the viewer
- by specifying a displacement vector pressing the middle button (like in a move
- command) and interclicking the right button.
-
- Moving and resizing a viewer. A viewer can be enlarged or shrunk by placing
- the cursor into the title bar and then moving it, while holding the left
- button to the desired new position. If the viewer is to be moved, additionally
- interclick the middle button.
-
- The following table shows a summary of the mouse actions:
-
- primary button: left middle right
- interclick:
- none draw line/set caret move select
- left - copy select
- middle copy text - copy text
- right shift plane - -
-
-
- Menu Commands
-
- The following commands from the tool package Draw are displayed in the menu
- (title bar) of every graphic viewer. They are activated by being pointed at
- and by clicking the middle button.
-
- Draw.Cleanup The entire drawing is repainted.
-
- Draw.Delete The selected elements are deleted.
-
- Draw.Store The drawing is written as file with the name shown in
- the title bar. The original file is renamed by changing
- its extension to Bak.
-
- The subsequent commands pertain to attributes of drawing elements, such as
- line width, text font, and color. The Set-commands determine the respective
- attributes of subsequently created elements, the Change-commands to those of
- the current selection in the marked viewer.
-
- Draw.SetWidth w Default width = 1, 0 < w < 7.
- System.SetFont fontname Default = Syntax10.Scn.Fnt
- System.SetColor c Default = white
- Draw.ChangeWidth w (0 < w < 7)
- Draw.ChangeFont fontname
- Draw.ChangeColor c
-
- The SetColor and ChangeColor commands either take a color number in the range
- 1.. 15 or a string as parameter. The latter case serves to select the color
- from the character immediately following the caret position (see Draw.Tool).
-
-
- Macros
-
- A macro is a (small) drawing that can be identified as a whole and be used
- as an element within a (larger) drawing. Macros are typically stored in
- collections called libraries, from where they can be selected and copied
- individually.
-
- Draw.Macro lib mac The macro mac is selected from the library
- named lib and inserted in the drawing at the
- caret's position. It is automatically selected
- and shown in inverse video.
-
- Draw.Directory libfile Lists the macro names contained in
- the named library file.
-
- The following commands are used when new macros are to be constructed and
- inserted in a (possibly new) library.
-
- Draw.OpenMacro lib mac The elements of macro mac from library lib
- are inserted at the caret's position. They
- can now be selected individually.
-
- Draw.MakeMacro lib mac 1. Select all elements that are to belong to
- the new macro.
- 2. Set the caret where the macro's origin is
- to be.
- 3. Place a secondary caret at the macro's
- opposite corner by using the left button
- and interclicking the right button.
- 4. Activate the MakeMacro command. The macro
- is added to the library lib and carries
- the name mac.
-
-
- Further Commands
-
- The following commands are listed in the text Draw.Tool, but may appear in
- any text.
-
- Draw.Reset The drawing plane in the marked viewer (*) is
- reset such that the plane's origin lies in the
- lower left corner.
-
-
- Draw.Store name The drawing in the marked viewer is stored as
- a file with the specified name.
-
- Draw.Print Servername * The drawing in the marked viewer is printed by
- the named print server.
-
- Draw.Print Servername { Filename }. The named files are printed.
-
- Draw.SetGrid n Inside graphic viewers, the cursor moves in
- discrete steps on an invisible grid. The
- distance between grid points is initially 4
- pixels; it can be set by this command to 2 - n
- pixels (0 <= n < 4).
-
- Draw.CarPos Displays the numeric coordinates of the
- caret in the log viewer.
-
- Draw.MakeLib lib filename The library lib is written as a file.
- Important Note: The written collection of
- macros contains only those macros that had
- previously been inserted by the commands
- Draw.Macro and Draw.MakeMacro. Hence, if
- new macros are to be added to an existing
- library file, the entire file must be read
- first by Draw.Macro applied to each existing
- macro element. This is typically done by
- opening a drawing containing all macros of
- the library file.
-
- Macros are typically used for drawing electronic circuits. The basic library
- file containing frequently used TTL circuits is called TTL0.Lib, and a drawing
- showing its elements is called TTL0.Graph.
-
-
- Rectangles
-
- Rectangles can be created as individual elements and are frequently used as
- frames around a set of other elements. They consist of four lines which are
- selectable as a unit. The attribute commands SetWidth, SetColor, ChangeWidth,
- and ChangeColor also apply to rectangles. Rectangles are selected by pointing
- at their lower left corner.
-
- Rectangles.New 1. Place the caret where a corner of the new rectangle
- is to lie (left button).
- 2. Place a secondary caret where the opposite corner is
- to lie by interclicking the right button.
- 3. Activate the command.
-
- Rectangles may be filled with a shade pattern. The shade is specified as a
- number s (0 # s # 9).
-
- Rectangles.SetShade s default = 0: no shading
- Note: In the current implementation, all shades
- appear as the same pattern on the display but
- differ on the printer.
-
-
- Circles
-
- Further graphic elements are circles and ellipses.
-
- Circles.New 1. Place the caret where the center is to lie.
- 2. Place a secondary caret to the right of the center.
- This position will lie on the circle.
- 3. Activate the command.
-
- If another secondary caret is positioned above the center, an ellipse is
- generated instead of a circle.
-
-
- Installing the Draw system
-
- The following modules are needed to install the system:
- Draw.Obj GraphicFrames.Obj Graphics.Obj Draw.Tool
-
- If rectangles are to be used:
- Rectangles.Obj
-
- and if circles are to be used:
- Circles.Obj Display1.Obj
-
- For drawing digital circuit diagrams, the following fonts and library is
- required: (also Rectangles)
- Syntax8.Scn.Fnt Elektra.Scn.Fnt TTL0.Lib
-
- Furthermore, there exist the following files:
- Symbols.Graph TTL0.Graph
-
-
- The Paint Tool Package
-
- The Paint package serves the purpose of displaying and editing digitized
- pictures. Its scheme of functionality and its user interface follow as
- closely as possible the lines of the text and graphic packages.
-
-
- The mouse-controlled built-in commands are these:
-
- Summary of mouse-controlled commands
-
- primary button left middle right
- ----------------------------------------------------------------------------
- secondary button
- none set caret pen/selection move select
- left - pen draw delete
- mid copy selection - copyover
- right multiple caret pen erase -
-
- The associated tool package augments the set of built-in picture editing
- operations by the following commands.
-
- Paint.Open {/w h}
- opens a viewer in the user track displaying the specified picture. The picture
- is alternatively specified by a parameter on the command line or by the most
- recent selection of a name. If none exists, the name Paint.Open is assumed as
- default.
-
- Paint.Zoom
- zooms in a picture to such a size that the most recent selection is completely
- visible, or if no selection exists, zooms out the picture.
-
- Paint.Print
- prints the named file. It assumes that the correct user identification has
- previously been installed.
-
- Paint.Scale
- opens a copy of the current picture scaled to the size of the most recent
- selection, or, if no selection exists, sized down to the size of the actual
- picture.
-
- Paint.Draw vertical line / horizontal line
- draws a vertical (horizontal) line starting at the first caret and ending at
- the second.
-
- Paint.SetColor | white | black | white invert
- applies the color specified by the parameter to subsequently executed
- operations.
-
- Paint.SetPen w h
- sets pen width and height to w and h.
-
- Paint.Fill n
- fills an area with specified pattern.
-
- Paint.SetGrid n
- sets the distance of grid points to n. Caret moves in discrete steps on the
- specified grid.
-
- Paint.SetFont FontName
- applies the font specified by the parameter to subsequently typed characters.
-
-
- The subsequent sections conclude this chapter. They provide a concise
- functional description in tabular form of the most important currently
- available non-editing tools.
-
-
- The System Tool Package
-
- This package features system related commands. Among them are procedures to
- display tools in the form of viewers, to close viewers and tracks, to define
- and display all kinds of system oriented parameters, and to provide basic
- utilities for the manipulation of named files. In addition, the system tool
- package contains a trap handler that is implicitly called after a trap has
- occured. It displays the current state of the system stack.
-
-
- System.Open
- opens a viewer in the system track displaying the specified tool. The tool is
- alternatively specified by a parameter on the command line or, in the case of
- a "^" following the command name, by the most recent selection of a name. If
- none exists, a default name is taken.
-
- System.OpenLog
- opens a viewer in the system track displaying the system-wide log. Notice
- that the log is updated regardless of its visibility. Therefore, the log
- always shows the complete history of the current session.
-
- System.Copy
- opens a copy of the original viewer displaying the same instance of contents.
-
- System.Grow
- lets the viewer grow to the size of a whole track or, if applied to a viewer
- already filling a track, to the size of the whole display. Note that the
- original constellation will be reestablished when the grown viewer is
- later removed.
-
- System.Close
- removes the marked viewer from the display, or, if called from the menu line
- of a text viewer, removes the own viewer.
-
- System.CloseTrack
- closes the marked track, i.e. removes all viewers in this track.
-
- System.Recall
- reopens the most-recently (perhaps erronously) closed viewer..
-
- System.Time
- displays the current date and time. If date and time parameters dd.mm.yy
- hh.mm.ss (day, month, year and hour, minute, second) immediately follow
- the command name, System.Time first sets date and time accordingly.
-
- System.Watch
- displays the amount of currently used disk space and memory resources.
-
- System.Collect
- initiates a subsequent garbage collection.
-
- System.Free
- unloads every module specified by the parameter list. If a module name is
- immediately followed by *, imported modules are also unloaded.
-
- System.ShowModules
- displays a map of all currently loaded modules.
-
- System.ShowCommands ModName
- displays a list of all commands exported by this module.
-
- System.State ModName
- displays the global data of the specified module.
-
- System.SetUser
- accepts the user's identification in the form UserName "/" Password without
- echoing it on the display. UserName is up to eight characters long (initials
- in most cases). The password is an arbitrary string.
-
- System.Directory
- displays the selection of all disk files whose name match the template
- specified by the parameter. The parameter is a string and may contain the
- symbol "*" as a wildcard. If option "d" is specified ("/d" immediately
- following the parameter) additional information about file sizes and dates
- is displayed. In the case of a "^" following the command name the parameter
- is taken from the current selection.
-
- System.CopyFiles
- processes a parameter list of pairs A => B. Copies each file A to B. In the
- case of a "^" following the command name the parameter is taken from the
- current selection.
-
- System.RenameFiles
- processes a parameter list of pairs A => B. Renames each file A to B. In the
- case of a "^" following the command name the parameter is taken from the
- current selection.
-
- System.DeleteFiles
- deletes each file specified by the parameter list. In the case of a "^"
- following the command name the parameter (a single file name) is taken
- from the current selection.
-
- System.SetFont
- applies the font specified by the parameter to subsequently typed characters.
-
- System.SetColor
- applies the color specified by the parameter to subsequently typed characters.
-
- System.SetOffset
- applies the vertical offset specified by the parameter to subsequently typed
- characters.
-
-
- The Compiler Tool Package
-
- This package contains the Oberon compiler. The result of compilations is shown
- in the log viewer. Possible errors are listed in the log viewer together with
- their position in the source text. In order to locate the error within the
- source text, use Edit.Locate in the log viewer's title menu. The compiler
- tool exports a single command:
-
- Compiler.Compile
- compiles all texts specified by the parameter list. Names in the parameter
- list refer to text files, the symbol * to the text in the marked viewer.
- In the case of a "^" fol lowing the command name the parameter list is
- taken from the current selection. The following options are available:
- "/x" (index check off), "/v" (check integer overflow), "t" (type guards off),
- "/s" (allow change of symbol file), and "/d" (provide debugging information).
-
-
- The Miscellaneous Tool Package
-
- This package provides utilities to convert files, determine statistical data,
- and demonstrate system behavior.
-
- Miscellaneous.BootLoad FileName
- downloads the named bootfile to boot sectors on disk.
-
- Miscellaneous.ConvertBlanks
- converts all text files specified by the parameter list by replacing pairs of
- leading spaces by tab characters. "^" following the command name refers to the
- current selection (a single file name).
-
- Miscellaneous.ConvertTabs
- converts all text files specified by the parameter list by replacing leading
- tab characters by pairs of spaces. "^" following the command name refers to
- the current selection (a single file name).
-
- Miscellaneous.Cleanup
- converts all text files specified by the parameter list into ordinary Ascii
- files i.e. deletes formatting information and removes non-printable characters. "^" following the command
- name refers to the current selection (a single file name).
-
- Miscellaneous.CountLines
- counts the lines of all text files specified by the parameter list. "^"
- following the command name refers to the current selection (a single file
- name).
-
- Miscellaneous.GetObjSize
- extracts code size and data size from all object files specified by the
- parameter list. "^" following the command name refers to the current
- selection (a single file name).
-
- Miscellaneous.Snapshot
- takes a snapshot of the current state of the screen display and stores it in
- the two files specified in the parameter list. The first file contains the
- lower half and the second file the upper half of the display.
-
- Miscellaneous.Trap
- forces a trap, thereby demonstrating the trap display mechanism.
-
-
- The Backup Tool Package
-
- This package handles the transfer between main disk and diskette. It supports
- diskettes conforming to a double-sided MSDOS-like format.
-
- Backup.Format
- formats an arbitrary double-sided diskette. This command is protected. Remove
- the ! before executing this command.
-
- Backup.Init
- initializes a formatted diskette to MSDOS-like Oberon format. This command is
- protected. Remove the ! before executing this command.
-
-
- Backup.Directory
- displays the directory of the currently loaded diskette.
-
- Backup.DeleteFiles
- deletes all files specified by the parameter list from the currently loaded
- diskette.
-
- Backup.ReadAll
- reads all files from the currently loaded diskette.
-
- Backup.ReadFiles
- reads all files specified by the parameter list from the currently loaded
- diskette.
-
- Backup.WriteFiles
- writes all files specified by the parameter list to the currently loaded
- diskette.
-
- Backup.ConvertToMSDOS
- converts the currently loaded diskette to the official MSDOS-format P9 for 3.5
- inch Diskettes. MSDOS-restrictions for file-names are enforced (length of
- filename <= 8, length of extension <= 3, upper-case characters only.
-
- Backup.ConvertFormMSDOS
- converts the currently loaded diskette from the official MSDOS-format P9 for
- 3.5 inch Diskettes to the Oberon-format.
-
-
- The Net Server Tool Package
-
- This package supports electronic mail and transfers of files over the local
- network. It also allows a workstation to become a file server itself. It
- assumes that the correct user identification has previously been installed.
-
- Net.Mailbox
- opens a viewer Mailbox.Text presenting an overview of all messages currently
- in the mailbox.
-
- Net.DeleteMail
- appearing in the header of viewer Mailbox.Text, deletes the message whose
- entry is selected.
-
- Net.ReceiveMail
- appearing in the header of viewer Mailbox.Text, opens a viewer displaying
- the message whose entry is selected.
-
- Net.SendMail
- sends the message displayed in the marked viewer. The syntactical definition
- of a message is as follows:
-
- message = recipient {recipient} [subject] text.
- recipient = "To:" AddressList | "cc:" AddressList.
- AddressList = address {"," address}.
- subject = "Re:" text.
-
- address is an RFC-name (@-notation) identifying an individual recipient or the
- name of a list of recipients that is stored on the mail server. Instead of @,
- ! and % are also accepted. No @-part is needed, if the recipient is within the
- Ceres-network.
-
- Net.SendFiles
- sends a sequence of files to a remote file server station. The first name in
- the parameter list identifies the desired file server and the remaining names
- define the sequence of desired files. By prefixing their names files may be
- sent to specific subdirectories. For example, the specification P:F.X implies
- an automatic name translation from F.X (on the master) to P.F.X (on the server).
- If "^" immediately follows the server name the parameter list is taken from the
- current selection.
-
- Net.ReceiveFiles
- gets a sequence of files from a remote file server station. The first name in
- the parameter list identifies the desired file server and the remaining names
- define the sequence of desired files. Again, prefixing and associated automatic
- name-translation allows files to be received from specific subdirectories. For
- example, the specification P:F.X implies an automatic name translation from
- P.F.X (on the server) to F.X (on the master). If "^" immediately follows the
- server name the parameter list is taken from the current selection.
-
- Net.DeleteFiles
- deletes a list of files from a remote file server. The first entry in the
- parameter list is the server name.
-
- Net.SendMsg partner message
- sends a one-line-message to the specified partner.
-
- Net.GetTime
- gets the time from the server and adjusts the local clock.
-
- Net.SetPassword server "password"
- installs new password in the server.
-
- Net.StartServer
- sets the workstation in server mode, i.e. allows access from other stations in
- the net.
-
- Net.StopServer
- terminates server mode
-
- Net.Unprotect
- allows write-access from other stations.
-
- Net.WProtect
- disallows write-access from other stations (default mode).
-
-
- The ColorSystem Tool Package
-
- This package provides utilities to inspect and define parameters of the color
- display. It should only be used if a color display is physically installed.
-
- ColorSystem.Init
- initializes the color display screen.
-
- ColorSystem.InitColors
- initializes the color palette.
-
- ColorSystem.ShowColors
- opens a viewer displaying the color palette. The viewer is editable.
-
- ColorSystem.LoadColors PalName
- loads the specified color palette.
-
- ColorSystem.StoreColors PalName
- stores the current palette under the name specified.
-
- ColorSystem.SetCursor x
- sets the color cursor to mode x. x = "^": arrow only, x = "+": crosshair only,
- x = "*": arrow and crosshair.
-
-
- Guide for Programmers of Commands
-
- In Oberon's modular hierarchy we recognize the following structural entities:
- The inner core, the outer core, the text system, the graphic system, the
- picture system, and a collection of tools.
-
-
- Oberon's module hierarchy
-
- Tool Packages
- Net Backup Compiler System Miscellaneous ColorSystem
- Edit Draw Paint
-
- Text System Graphic System Picture System
- TextFrames GraphicFrames PictureFrames
- Graphics Pictures MenuViewers
-
- Outer Core Inner Core Printer Oberon Texts
- Modules Fonts Files FileDir Math
- MathL Reals Viewers Drivers Kernel V24 SCC Diskette
- Input Display
-
- The responsability of the inner core comprises memory management, file
- management, and program loading. The outer core additionally provides
- device drivers for network ports, keyboard, mouse, and display screens.
- Other parts of the outer core are viewer manager, elementary text management,
- and support for (remote) printing. Module Oberon represents the main interface
- between the outer core and its clients. It includes sections that are devoted
- to the current system configuration, to default strategies for track allocation
- and viewer placement, and to the support of command execution.
-
- Module Display stands at the bottom of the display system hierarchy. The
- display area is considered as a plane with x and y coordinates. It includes
- both a black-and-white area and a color area. Raster operation s are used to
- generate and copy rectangular areas on the display plane. Sections of the
- plane can be made visible by display control procedures. The visible parts
- of the display plane are structured as tracks and viewers, and they are
- managed by the viewer manager Viewers.Module Oberon defines a standard
- layout featuring one user track and one system track per display screen.
- Finally, module MenuViewers is a high-level viewer manager for standard
- viewers consisting of a title bar and a rectangular main area surrounded
- by a thin frame. Both title bar and main area are so-called frames.
- While the title bar is almost always a text frame (see next paragraph), the
- type of the main frame depends on the kind of viewer.
-
- The text system, the graphic system, and the picture system are identical
- in structure. Each consists of a triple of linearly dependent modules. In
- the case of texts they are called Texts, TextFrames, and Edit. Texts
- defines the object type Text and exports intrinsic operations on texts.
- TextFrame s defines the object type TextFrames.Frame and handles
- representations of texts within sub-frames of viewers. Edit provides
- additional (non-built-in) text-editing operations.
-
- Modules at the top (like Edit ) are tool packages. Typically, a tool
- package merely exports a collection of commands in the form of parameterless
- procedures. Tool modules make intensive use of facilities provided by lower
- level modules, in particular by the viewer system, the text system, and the
- central system module Oberon. It is essential that usual commands strictly
- operate on texts or graphics instead of accessing keyboard or screen directly.
-
- We understand this chapter as a tutorial on implementing tool packages.
- First, we give a commented overview of the definitions of the most important
- lower-level modules. Then, we shall exemplify their usage by some typical
- excerpts from existing tools.
-
-
- The Display System
-
-
- DEFINITION Display; (*display driver*)
-
- CONST
- black = 0;
- white = 15;
- replace = 0;
- paint = 1;
- invert = 2; (*operation modes*)
-
- TYPE
- Frame = POINTER TO FrameDesc;
- FrameMsg = RECORD
- END; (*base type of messages to frames*)
-
- Pattern = LONGINT; (*pointer to pattern descriptor*)
-
- (*PatternDesc = RECORD
- w, h: SHORTINT;
- raster: ARRAY (w + 7) DIV 8 * h OF BYTE
- END*)
-
- Font = POINTER TO Bytes;
- Bytes = RECORD
- END;
-
- Handler = PROCEDURE (f: Frame, VAR msg: FrameMsg);
-
- FrameDesc = RECORD (*base type of frames*)
- dsc, next: Frame;
- X, Y, W, H: INTEGER;
- handle: Handler
- END;
-
- VAR
- Unit: LONGINT; (*RasterUnit = Unit/36000 mm*)
- Left, (*left margin of black-and-white maps*)
- ColLeft, (*left margin of color maps*)
- Bottom, (*Bottom of primary map*)
- UBottom, (*Bottom of secondary map*)
- Width, (*map width*)
- Height (*map height*)
- : INTEGER;
-
- arrow, star, cross, downArrow, hook: Pattern;
-
- PROCEDURE Map (X: INTEGER): LONGINT; (*address of map at X*)
- PROCEDURE SetMode (X: INTEGER; s: SET); (*set mode of map at X*)
- (*black & white display: 0: display disable,
- 1: display secondary map,
- 2: inverse video*)
-
- (*color display*)
-
- PROCEDURE SetColor (col, red, green, blue: INTEGER);
- (*col < 0: overlay color*)
- PROCEDURE GetColor (col: INTEGER; VAR red, green, blue: INTEGER);
-
- PROCEDURE SetCursor(mode: SET); (*color cursor; 0: crosshair, 1: arrow*)
-
- PROCEDURE InitCC; (*initialize color crosshair to full screen*)
- PROCEDURE InitCP; (*initialize color pattern to arrow shape*)
-
- PROCEDURE DefCC (X, Y, W, H: INTEGER); (*define window for color crosshair*)
- PROCEDURE DefCP (VAR raster: ARRAY OF BYTE);
- (*define 64 x 64 raster for color pattern marker*)
- PROCEDURE DrawCX (X, Y: INTEGER); (*draw color cursor at X, Y*)
- PROCEDURE FadeCX (X, Y: INTEGER); (*fade color cursor at X, Y*)
-
- (*fonts*)
-
- PROCEDURE GetChar(f: Font;
- ch: CHAR;
- VAR dx, x, y, w, h: INTEGER;
- VAR p: Pattern);
- (*get box x, y, w, h, width dx, and raster data p of character ch in font f*)
-
- (*raster operations*)
-
- PROCEDURE CopyBlock (SX, SY, W, H, DX, DY, mode: INTEGER);
- (*copy source block SX, SY, W, H to destination DX, DY using operation
- mode. A block is given by its lower left corner X, Y and its dimension
- W, H*)
-
-
- PROCEDURE CopyPattern (col: INTEGER; pat: Pattern; X, Y, mode: INTEGER);
-
- (*copy pattern p in color col to X, Y using operation mode
- col = 0: black; col = 15: white*)
-
- PROCEDURE ReplPattern (col: INTEGER;
- pat: Pattern; X, Y, W, H, mode: INTEGER);
- (*replicate pattern p in color col into block X, Y, W, H using operation
- mode, proceeding from left to right and from bottom to top, starting
- at lower left corner*)
-
- PROCEDURE ReplConst (col: INTEGER; X, Y, W, H, mode: INTEGER);
- (*place "ones" in color col into block X, Y, W, H using operation mode*)
-
-
- END Display.
-
-
- Remarks:
-
- 1. The Ceres computer features a monochrome display whose position (lower left
- corner) is specified by the variables Left and Bottom, and whose width and
- height are given by the variables Width and Height.
- In fact, the drawing area is bigger; its y-coordinate ranges from -1248 to
- 799. Two sections can be made visible by the display control procedures,
- the first being characterized by {y| -1024 <= y < -224}, and the other by
- {y| 0 <= y < 800}.
-
- 2. If a color display is installed, the module's raster procedures can be used
- to generate and copy areas on the color screen. The position of the color
- area (lower left corner) is specified by the variables ColLeft and Bottom;
- its width and height are the same as for the monochrome display.
-
- 3. The postulated preconditions upon procedure parameters are not checked by
- the module; this is left to the calling modules which are held responsible
- for robustness.
-
- 4. Notice that there are the following implementation restrictions of the
- raster operations:
-
- ReplConst
- Color display: paint mode treated as replace mode.
-
- ReplPattern
- Pattern width w ignored and taken as 32 on monochrome and as 16 on color
- display. 0 <= h < 256 on monochrome, 0 <= h <= 16 on color display.
- Color display: x and x+w should be even, otherwise 1 is subtracted.
-
- CopyPattern
- Replace mode treated like paint mode.
-
- 0 < w <= 32, 0 <= h < 256.
-
- CopyBlock
- All modes treated as replace mode.
-
-
- ------------------------------------------------------------------------
-
- DEFINITION Viewers; (*viewer manager*)
-
- IMPORT Display;
-
- CONST
-
- restore = 0; modify = 1; suspend = 2;
- (*message ids referring to the following message type*)
-
- TYPE
-
- ViewerMsg = RECORD (*message sent to viewers on viewer events*)
- (Display.FrameMsg)
- id: INTEGER;
- X, Y, W, H: INTEGER;
- state: INTEGER
- END;
-
- Viewer = POINTER TO ViewerDesc;
-
- ViewerDesc = RECORD (* viewer descriptor extends Display.FrameDesc*)
- (Display.FrameDesc)
- state: INTEGER
- END;
-
- (*state > 1: displayed
- state = 1: filler
- state = 0: closed
- state < 0: suspended*)
-
- VAR curW, minH: INTEGER;
- (*current width of logical display, minimum viewer height*)
-
- PROCEDURE InitTrack (W, H: INTEGER; Filler: Viewer);
- (*append to current logical display and init track of width W and height H
- and install Filler*)
-
- PROCEDURE OpenTrack (X, W: INTEGER; Filler: Viewer);
- (*open new track overlaying span of [X, X + W[*)
-
- PROCEDURE CloseTrack (X: INTEGER);
- (*close track at X and restore overlaid tracks*)
-
- PROCEDURE Locate (X, H: INTEGER; VAR fil, bot, alt, max: Display.Frame);
- (*in the track at X locate the following viewers:
- filler fil,
- bottom viewer bot,
- an alternative viewer alt of height >= H,
- viewer max of maximum height*)
-
- PROCEDURE Open (V: Viewer; X, Y: INTEGER);
- (*open new viewer V with top at Y in track at X*)
-
- PROCEDURE Change (V: Viewer; Y: INTEGER);
- (*expand or shrink viewer V to new top Y*)
-
- PROCEDURE Close (V: Viewer);
- (*remove viewer V from the display*)
-
- PROCEDURE Recall (VAR V: Viewer);
- (*recall most recently closed viewer*)
-
-
- PROCEDURE This (X, Y: INTEGER): Viewer;
- (*return viewer at X, Y*)
-
- PROCEDURE Next (V: Viewer): Viewer;
- (*return next upper neighbour of V*)
-
- PROCEDURE Broadcast (VAR M: Display.FrameMsg);
- (*send message M to all visible viewers*)
-
- END Viewers.
-
- --------------------------------------------------------------------------
-
- DEFINITION MenuViewers;
-
- IMPORT Display, Viewers;
-
- CONST extend = 0; reduce = 1; (*message ids*)
-
- TYPE Viewer = POINTER TO ViewerDesc;
-
- ViewerDesc = RECORD (Viewers.ViewerDesc)
- menuH: INTEGER (*height of menu frame*)
- END;
-
- ModifyMsg = RECORD (Display.FrameMsg)
- id: INTEGER; (*extend or reduce*)
- dY, Y, H: INTEGER (*translation vector dY; new Y and H*)
- END;
-
- VAR Ancestor: Viewer; (*current menu viewer*)
-
- PROCEDURE Handle (V: Display.Frame; VAR M: Display.FrameMsg);
- (*standard handler for menu viewers*)
-
- PROCEDURE New (Menu, Main: Display.Frame; menuH, X, Y: INTEGER): Viewer;
- (*create and open at X, Y new menu viewer containing frames Menu and
- Main*)
-
- END MenuViewers.
-
-
- Remark:
-
- Messages to menu viewers not affexting size and position are passed on to
- their subframes. The ancestor viewer is made available to the subframe
- handlers via the variable Ancestor. MenuViewers also creates new messages
- of type ModifyMsg requesting subframes to change size or vertical position
- (or both). dY represents a vertical translation vector, and Y and H specify
- the new position and height respectively.
-
-
- --------------------------------------------------------------------------
-
-
- The Text System
-
- DEFINITION Fonts; (*font loader*)
-
- IMPORT Display;
-
-
- TYPE
-
- Name = ARRAY 32 OF CHAR;
-
- Font = POINTER TO FontDesc;
-
- FontDesc = RECORD
- name: Name; (*file name*)
- height, minX, maxX, minY, maxY: INTEGER;
- (*characteristic data*)
- raster: Display.Font (*raster data*)
- END;
-
- (*height = minimum distance between text lines,
- minX, maxX, minY, maxY are minima and maxima of X and Y,
- if all character boxes of the font are placed at the origin 0, 0*)
-
- VAR Default: Font; (*the default font*)
-
- PROCEDURE This (name: ARRAY OF CHAR): Font;
- (*font with name given*)
-
- END Fonts.
-
- --------------------------------------------------------------------------
-
- DEFINITION Texts; (*text manager*)
-
- IMPORT Files, Fonts;
-
- CONST
-
- (*symbol classes, see def. of type Scanner*)
-
- Inval = 0; (*invalid symbol*)
- Name = 1; (*name s (length len)*)
- String = 2; (*literal string s (length len)*)
- Int = 3; (*integer i (decimal or hexadecimal)*)
- Real = 4; (*real number x*)
- LongReal = 5; (*long real number y*)
- Char = 6; (*special character c*)
-
- replace = 0;
- insert = 1;
- delete = 2; (*op-codes*)
-
-
- TYPE
-
- Text = POINTER TO TextDesc;
-
- Notifier = PROCEDURE (T: Text; op: INTEGER; beg, end: LONGINT);
-
- TextDesc = RECORD
- len: LONGINT; (*text length*)
- notify: Notifier (*of editing operations*)
- END;
-
- Reader = RECORD
- (Files.Rider)
- eot: BOOLEAN;
- fnt: Fonts.Font; (*font of current character*)
- col: SHORTINT; (*color of current character*)
- voff: SHORTINT (*vertical offset*)
- END;
-
- Scanner = RECORD
- (Reader)
- nextCh: CHAR;
- line: INTEGER;
- class: INTEGER;
- i: LONGINT;
- x: REAL;
- y: LONGREAL;
- c: CHAR;
- len: SHORTINT;
- s: ARRAY 32 OF CHAR
- END;
-
- (*used to convert a text into a stream of symbols.
- Symbol classes are defined under CONST*)
-
- Buffer = POINTER TO BufDesc;
-
- BufDesc = RECORD
- len: LONGINT (*buffer length*)
- END;
-
- (*used to write a stream of textual data in a buffer*)
-
- (*used to store a stretch of a text*)
-
- Writer = RECORD
- (Files.Rider)
- buf: Buffer; (*associated buffer*)
- fnt: Fonts.Font; (*current font*)
- col: SHORTINT; (*color of current character*)
- voff: SHORTINT (*vertical offset*)
- END;
-
- PROCEDURE Load (T: Text; f: Files.File; pos: LONGINT; VAR len: LONGINT);
- (*load text block from file f at position pos to text T*)
-
- PROCEDURE Open (T: Text; name: ARRAY OF CHAR);
- (*open text T from disk file specified by name; open new text if name = ""*)
-
- PROCEDURE OpenBuf (B: Buffer);
- (*open new text buffer B*)
-
- PROCEDURE OpenReader (VAR R: Reader; T: Text; pos: LONGINT);
- (*open text reader R and set it up at position pos in text T*)
-
- PROCEDURE Read (VAR R: Reader; VAR ch: CHAR);
- (*read next character in ch*)
-
- PROCEDURE Pos (VAR R: Reader): LONGINT;
- (*return reader's position within its text*)
-
- PROCEDURE Store (T: Text; f: Files.File; pos: LONGINT; VAR len: LONGINT);
- (*store text T on disk file f at position pos*)
-
- PROCEDURE Save (T: Text; beg, end: LONGINT; B: Buffer);
- (*append stretch [beg, end[ of text T to buffer B*)
-
- PROCEDURE Copy (SB, DB: Buffer);
- (*append copy of source buffer SB to destination buffer DB*)
-
- PROCEDURE ChangeLooks (T: Text;
- beg, end: LONGINT;
- sel: SET;
- fnt: Fonts.Font;
- col, voff:SHORTINT);
- (*change character attributes within stretch [beg, end[ of text T. sel
- selects attributes to be changed. 0, 1, 2 IN sel = fnt, col, voff selected*)
-
- PROCEDURE Insert (T: Text; pos: LONGINT; B: Buffer);
- (*insert buffer B in text T at position pos*)
-
- PROCEDURE Append (T: Text; B: Buffer);
- (*append buffer B to text T*)
-
- PROCEDURE Delete (T: Text; beg, end: LONGINT);
- (*delete stretch [beg, end[ of text T*)
-
- PROCEDURE Recall (VAR B: Buffer);
- (*recall previously deleted text*)
-
- PROCEDURE OpenScanner (VAR S: Scanner; T: Text; pos: LONGINT);
- (*open text scanner S and set it up at position pos in text T*)
-
- PROCEDURE Scan (VAR S: Scanner);
- (*read next symbol*)
-
- PROCEDURE OpenWriter (VAR W: Writer);
- (*open new writer W*)
-
- PROCEDURE SetFont (VAR W: Writer; fnt: Fonts.Font);
- (*set writer W to font fnt*)
-
- PROCEDURE SetColor (VAR W: Writer; col: SHORTINT);
- (*set writer W to color col*)
-
- PROCEDURE SetOffset (VAR W: Writer; voff: SHORTINT);
- (*set writer W to vertical offset voff*)
-
- PROCEDURE Write (VAR W: Writer; ch: CHAR);
- (*write character ch to W's buffer*)
-
- PROCEDURE WriteLn (VAR W: Writer);
- (*write end-of-line to W's buffer*)
-
- PROCEDURE WriteInt (VAR W: Writer; x, n: LONGINT);
- (*write integer x to W's buffer. Right adjust to n positions*)
-
- PROCEDURE W
- riteHex (VAR W: Writer; x: LONGINT);
- (*write integer x to W's buffer in hexadecimal form.
-
- PROCEDURE WriteString (VAR W: Writer; s: ARRAY OF CHAR);
- (*write string s to W's buffer*)
-
- PROCEDURE WriteReal (VAR W: Writer; x: REAL; n: INTEGER);
- (*write real number x to W's buffer. Use n positions*)
-
- PROCEDURE WriteRealFix (VAR W: Writer; x: REAL; n, k: INTEGER);
- (*write real number x to W's buffer in fixed-point form,
- using k positions for decimal fractions and n positions in total*)
-
-
- PROCEDURE WriteRealHex (VAR W: Writer; x: REAL);
- (*write real number x to W's buffer in hexadecimal form*)
-
- PROCEDURE WriteLongReal (VAR W: Writer; x: LONGREAL; n: INTEGER);
- (*write long real number x to W's buffer. Use n positions*)
-
- PROCEDURE WriteLongRealHex (VAR W: Writer; x: LONGREAL);
- (*write long real number x to W's buffer in hexadecimal form*)
-
- END Texts.
-
-
- Remark:
-
- Open does not create a text object nor does it install a notifier procedure.
- Both actions are left to the calling modules. Typically, a calling module
- first creates a text object (or an extension of it) by using NEW, and then
- installs a notifier procedure. The main purpose of notifier procedures is
- requesting the display to re-establish consistency after a change in a text
- has occurred.
-
- --------------------------------------------------------------------------
-
- DEFINITION TextFrames; (*text display*)
-
- IMPORT Display, Texts;
-
- TYPE
-
- Location = RECORD
- org, pos: LONGINT; (*line origin, position*)
- dx, x, y: INTEGER (*width and position of located character*)
- END;
-
- Frame = POINTER TO FrameDesc;
-
- FrameDesc = RECORD
- (Display.FrameDesc)
- text: Texts.Text; (*displayed text*)
- org: LONGINT; (*position in text of first displayed character*)
- col: INTEGER; (*background color*)
- lsp, asr, dsr: INTEGER; (*line spacing, ascender, descender*)
- left, right, top, bot: INTEGER; (*margins*)
- markH: INTEGER; (*margin width, position of mark*)
- time: LONGINT; (*time of latest selection*)
- mark, car, sel: INTEGER; (*state of mark, caret, selection*)
- carloc: Location; (*caret location*)
- selbeg, selend: Location (*locations of begin and end of selection*)
- END;
-
- (*mark < 0: arrow mark
- mark = 0: no mark
- mark > 0: position mark
- car = 0: caret not set
- car > 0: caret set
- sel = 0: no selection active
- sel > 0: selection active*)
-
- UpdateMsg = RECORD
- (Display.FrameMsg)
- id: INTEGER;
- text: Texts.Text;
- beg, end: LONGINT
- END;
-
- VAR menuH, barW, left, right, top, bot, asr, dsr, lsp: INTEGER;
- (*standard sizes*)
-
- PROCEDURE Restore (F: Frame);
- (restore frame F*)
-
- PROCEDURE Suspend(F: Frame);
- (*suspend frame F*)
-
- PROCEDURE Extend (F: Frame; newY: INTEGER);
- (*extend frame F to bottom newY*)
-
- PROCEDURE Reduce (F: Frame; newY: INTEGER);
- (*reduce frame F to bottom newY*)
-
- PROCEDURE Mark (F: Frame; mark: INTEGER);
- (*mark frame F as specified by mark*)
-
- PROCEDURE Show (F: Frame; pos: LONGINT);
- (*show text part containing position pos in frame F*)
-
- PROCEDURE Pos (F: Frame; X, Y: INTEGER): LONGINT;
- (*convert coordinates X, Y to text position*)
-
- PROCEDURE SetCaret (F: Frame; pos: LONGINT);
- (*set caret in frame F at position pos*)
-
- PROCEDURE TrackCaret (F: Frame; X, Y: INTEGER; VAR keysum: SET);
- (*track caret in frame F, starting from X, Y, and return mouse-keys
- pressed*)
-
- PROCEDURE RemoveCaret (F: Frame);
- (*remove caret from frame F*)
-
- PROCEDURE SetSelection (F: Frame; beg, end: LONGINT);
- (*select text stretch [beg, end[ in F*)
-
- PROCEDURE TrackSelection (F: Frame; X, Y: INTEGER; VAR keysum: SET);
- (*track selection in frame F, starting from X, Y, and return mouse-keys
- pressed*)
-
- PROCEDURE RemoveSelection (F: Frame);
- (*remove selection from frame F*)
-
- PROCEDURE TrackLine (F: Frame;
- X, Y: INTEGER;
- VAR org: LONGINT;
- VAR keysum: SET);
- (*track text line in frame F, starting from X, Y, and return line-origin
- and mouse-keys pressed*)
-
- PROCEDURE TrackWord (F: Frame;
- X, Y: INTEGER;
- VAR pos: LONGINT;
- VAR keysums: SET);
- (*track text word in frame F, starting from X, Y,
- and return starting position and mouse-keys pressed*)
-
- PROCEDURE Replace (F: Frame; beg, end: LONGINT);
- (*text stretch [beg, end[ was replaced; update frame F*)
-
- PROCEDURE Insert (F: Frame; beg, end: LONGINT);
- (*text stretch [beg, end[ was inserted; update frame F*)
-
- PROCEDURE Delete (F: Frame; beg, end: LONGINT);
- (*text stretch [beg, end[ was deleted; update frame F*)
-
-
- (*---------------- message handling ----------------*)
-
- PROCEDURE NotifyDisplay (T: Texts.Text; op: INTEGER; beg, end: LONGINT);
-
- (*notify display manager of text status change*)
-
- PROCEDURE Call* (F: Frame; pos: LONGINT; new: BOOLEAN);
- (*call command specified at pos in frame F. new forces loading of newest
- version*)
-
- PROCEDURE Write* (F: Frame;
- ch: CHAR; fnt:
- Fonts.Font;
- col, voff: SHORTINT);
- (*write character ch with given attributes at caret position*)
-
- PROCEDURE Defocus* (F: Frame); (F: Frame; ch: CHAR; fnt: Fonts.Font; col,voff: SHORTINT);
- (*remove caret*)
-
- PROCEDURE Neutralize* (F: Frame);
- (*remove marks*)
-
- PROCEDURE Modify* (F: Frame; id, dY, Y, H: INTEGER);
- (*vertically translate and extend or reduce frame F. id indicates type
- (extension or reduction), dy is a translation vector, and Y, H specify
- new location and height respectively*)
-
- PROCEDURE Open* (F: Frame; H: Display.Handler; T: Texts.Text; org: LONGINT;
- col, left, right, top, bot, asr, dsr, lsp: INTEGER);
- (*open new text frame F displaying text T starting from position org,
- with background color col, margins left, right, top, bot, and line
- geometry asr, dsr, lsp = ascender, descender line spacing.
- Install notifier H*)
-
- PROCEDURE Copy* (F: Frame; VAR F1: Frame);
- (*generate copy F1 of frame F. Initialize to empty frame*)
-
- PROCEDURE CopyOver* (F: Frame; text: Texts.Text; beg, end: LONGINT);
- (*copy over text stretch [beg, end[ to caret position in frame F*)
-
- PROCEDURE GetSelection* (F: Frame;
- VAR text: Texts.Text;
- VAR beg, end, time: LONGINT);
- (*get current text selection in frame F (if any)*)
-
- PROCEDURE Update* (F: Frame; VAR M: UpdateMsg);
- (*update display after editing operation*)
-
- PROCEDURE Edit* (F: Frame; X, Y: INTEGER; Keys: SET);
- (*track mouse and interpret editing commands*)
-
- PROCEDURE Handle* (F: Display.Frame; VAR M: Display.FrameMsg);
- (*standard handler for text frames*)
-
- PROCEDURE Text* (name: ARRAY OF CHAR): Texts.Text;
- (*create new displayed text from named file. Empty file name means empty
- text*)
-
- PROCEDURE NewMenu* (name, commands: ARRAY OF CHAR): Frame;
- (*create new menu frame containing listed commands*)
-
- PROCEDURE NewText* (text: Texts.Text; pos: LONGINT): Frame;
- (*create new standard text frame*)
-
- END TextFrames.
-
- --------------------------------------------------------------------------
-
-
- The Oberon Core
-
- DEFINITION Math; (*math library for reals*)
-
- CONST pi = 3.14159265; e = 2.71828182;
-
- PROCEDURE sqrt(x: REAL): REAL;
- PROCEDURE exp(x: REAL): REAL;
- PROCEDURE ln(x: REAL): REAL;
- PROCEDURE sin(x: REAL): REAL;
- PROCEDURE cos(x: REAL): REAL;
- PROCEDURE arctan(x: REAL): REAL;
-
- END Math.
-
- --------------------------------------------------------------------------
-
- DEFINITION MathL; (*math library for longreals*)
-
- CONST pi = 3.141592653589793D0;
- e = 2.718281828459045D0;
-
-
- PROCEDURE sqrt(x: LONGREAL): LONGREAL;
- PROCEDURE exp(x: LONGREAL): LONGREAL;
- PROCEDURE ln(x: LONGREAL): LONGREAL;
- PROCEDURE sin(x: LONGREAL): LONGREAL;
- PROCEDURE cos(x: LONGREAL): LONGREAL;
- PROCEDURE arctan(x: LONGREAL): LONGREAL;
-
- END MathL.
-
- --------------------------------------------------------------------------
-
- DEFINITION Files; (*file manager*)
-
- TYPE
- Handle = RECORD END ;
- File = POINTER TO Handle;
-
- (*A file is a sequence of bytes, accessed via (a pointer to) a handle.
- Files are stored on disk and may be referenced through a name entered
- in the file directory*)
-
- Rider = RECORD
- res: INTEGER;
- eof: BOOLEAN
- END;
-
- (*Elements of files are accessed through a rider, which has a position that
- is advanced when reading or writing data. The position is an integer
- between 0 and the length of the file to which the rider is attached.
- The fields eof and res serve as result parameters of file procedures.*)
-
- PROCEDURE Old(name: ARRAY OF CHAR): File;
- (*the file with the given name. NIL if the name is not in the directory*)
-
- PROCEDURE New(name: ARRAY OF CHAR): File;
- (*a new file with given name*)
-
- PROCEDURE Register(f: File);
- (*Close file f and register it under its name in the directory.
- If the name exists already, the corresponding old file is unregistered*)
-
-
- PROCEDURE Close(f: File);
-
- PROCEDURE Purge(f: File);
-
- PROCEDURE Length(f: File): LONGINT; (*the number of bytes in the file*)
-
- PROCEDURE Set(VAR r: Rider; f: File; pos: LONGINT);
- (*Associate rider r with file f at position pos. r.eof := FALSE*)
-
- PROCEDURE Read(VAR r: Rider; VAR x: BYTE);
- (*read byte and advance rider by one position. If at end, r.eof := TRUE and
- x := 0X*)
-
- PROCEDURE ReadBytes(VAR r: Rider; VAR x: ARRAY OF BYTE; n: INTEGER);
- (*read n bytes and advance rider by n positions.
- If at end, r.eof := TRUE and r.res := no. of bytes requested but not
- read.*)
-
- PROCEDURE Write(VAR r: Rider; x: BYTE);
- (*write byte and advance rider by one position*)
-
- PROCEDURE WriteBytes(VAR r: Rider; VAR x: ARRAY OF BYTE; n: INTEGER);
- (*write n bytes and advance rider by n positions*)
-
- PROCEDURE Pos(VAR r: Rider): LONGINT;
-
- PROCEDURE Base(VAR r: Rider): File;
-
- PROCEDURE Rename(old, new: ARRAY OF CHAR; VAR res: INTEGER);
- (*res = 0: renamed; res = 1: new name existed already and now denotes the
- renamed file; res = 2: old name not in directory; res = 3: name is illegal;
- res = 4: name is too long *)
-
- PROCEDURE Delete(name: ARRAY OF CHAR; VAR res: INTEGER);
- (*res = 0: deleted; res = 2: name not in directory;
- res = 3: name is illegal; res = 4: name is too long *)
-
- END Files.
-
- --------------------------------------------------------------------------
-
- DEFINITION Diskette; (*diskette manager*)
-
- TYPE EntryHandler = PROCEDURE (name: ARRAY OF CHAR;
- date, time, size: LONGINT);
-
- VAR res: INTEGER; (*result of file-oriented operation, error occurred =
- (res #0)*)
- err: SHORTINT; sect: LONGINT; busy: BOOLEAN; (*state of device driver*)
-
-
- (*device driver*)
- PROCEDURE Reset;
- PROCEDURE GetSector (sec: INTEGER; VAR buf: ARRAY OF BYTE; off: INTEGER);
- PROCEDURE PutSector (sec: INTEGER; VAR buf: ARRAY OF BYTE; off: INTEGER);
- PROCEDURE Format;
-
- (*directory handler*)
- PROCEDURE InitDir (format: CHAR); (*format for future extension*)
- PROCEDURE ReadDir;
- PROCEDURE WriteDir;
- PROCEDURE GetData (VAR date, time, nofFiles, nofClusters: INTEGER); (*get volume data*)
- PROCEDURE Enumerate (proc: EntryHandler);
-
- (*file handler*)
- PROCEDURE ReadAll;
- PROCEDURE ReadFile (name: ARRAY OF CHAR);
-
- PROCEDURE WriteFile (name: ARRAY OF CHAR);
- PROCEDURE DeleteFile (name: ARRAY OF CHAR);
-
- END Diskette.
-
- --------------------------------------------------------------------------
-
- DEFINITION Input; (*keyboard and mouse driver*)
-
- PROCEDURE Available(): INTEGER;
- (*the number of characters available from the keyboard*)
-
- PROCEDURE Read (VAR ch: CHAR);
- (*next character from keyboard*)
-
- PROCEDURE Mouse (VAR keys: SET; VAR x, y: INTEGER);
- (*current coordinates and key setting of mouse.
- 0 IN keys = right key pressed
- 1 IN keys = middle key pressed
- 2 IN keys = left key pressed*)
-
- PROCEDURE SetMouseLimits (w, h: INTEGER);
- (* define width and height of rectangle in which mouse moves*)
-
- PROCEDURE Time(): LONGINT;
- (* current system time in units of 1/300 sec*)
-
- END Input.
-
- --------------------------------------------------------------------------
-
- DEFINITION SCC; (*SCC driver*)
-
- (*Serial Communications Controller driver module (Zilog Z8530)
- Data are transmitted in blocks. Each block contains two parts: header and
- data *)
-
- TYPE Header = RECORD
- valid: BOOLEAN;
- dadr, sadr, typ: SHORTINT;
- len: INTEGER; (*of data following header*)
- destLink, srcLink: INTEGER (*link numbers*)
- END;
-
- (*dadr is the receiver's machine number, len is the length (number of bytes)
- of the data part. typ, destLink, and srcLink are not interpreted by SCC*)
-
- PROCEDURE Start(filter: BOOLEAN);
- (*initialise the SCC*)
-
- PROCEDURE Send(VAR head, buf: ARRAY OF BYTE);
- (*send buf[0]... buf[head.len-1] to head.adr*)
-
- PROCEDURE Available(): INTEGER;
- (*number of bytes available from receiver buffer. Buffer contains stream
- of received bytes, including headers and data parts*)
-
- PROCEDURE ReceiveHead(VAR head: ARRAY OF BYTE);
- (*read a header from the receiver buffer*)
-
- PROCEDURE Receive(VAR x: BYTE);
- (*read a byte from the receiver buffer*)
-
-
- PROCEDURE Skip(m: INTEGER);
- (*skip m bytes in the receiver buffer*)
-
- PROCEDURE Stop; (*turn SCC off*)
-
- END SCC.
-
- --------------------------------------------------------------------------
-
- DEFINITION V24; (*V24 driver*)
-
- (*interrupt-driven UART channel B*)
-
- PROCEDURE Start(CSR, MR2: CHAR);
- (* Clock Select Register:
- 66X: 1200 bps
- 88X: 2400 bps
- 0BBX: 9600 bps
-
- Mode Register 2:
- 7X: 1 stop bit
- 0FX: 2 stop bits *)
-
- PROCEDURE SetOP(s: SET); (*output port*)
- PROCEDURE ClearOP(s: SET);
- (* 0: DTR, 1: RTS *)
-
- PROCEDURE IP(n: INTEGER): BOOLEAN; (*input port*)
-
- PROCEDURE SR(n: INTEGER): BOOLEAN;
- (*Status Register. 0: Rx rdy, 2: Tx rdy, 4: overrun*)
-
- PROCEDURE Available(): INTEGER;
- PROCEDURE Receive(VAR x: BYTE);
- PROCEDURE Send(x: BYTE);
-
- PROCEDURE Break;
- PROCEDURE Stop;
-
- END V24.
-
- --------------------------------------------------------------------------
-
- DEFINITION Printer; (*printer interface*)
-
- VAR res: INTEGER; (*result*)
-
- PROCEDURE Open(VAR name, user: ARRAY OF CHAR; password: LONGINT);
- (*res = 0: opened, 1: no printer, 2: no link, 3: bad response, 4: no
- permission*)
-
- PROCEDURE Font (fno: SHORTINT; VAR name: ARRAY OF CHAR); (*install font*)
- PROCEDURE String (x, y: INTEGER; VAR s: ARRAY OF CHAR; fno: SHORTINT);
- (*place string*)
- PROCEDURE ContString (VAR s: ARRAY OF CHAR; fno: SHORTINT); (*place continuation string*)
- PROCEDURE Line (x, y, w, h: INTEGER); (*place horizontal or vertical line*)
- PROCEDURE XLine (x, y, dx, dy: INTEGER); (*place line of general direction*)
- PROCEDURE Circle (x, y, a, b: INTEGER); (*place circle or ellipsis*)
- PROCEDURE Shade (x, y, w, h, col: INTEGER); (*shade area*)
- PROCEDURE Picture (x, y, w, h, mode: INTEGER; adr: LONGINT); (*place picture*)
- PROCEDURE Page(nofcopies: INTEGER); (*print current page*)
-
- PROCEDURE Close; (*close connection*)
-
- END Printer.
-
- --------------------------------------------------------------------------
-
- DEFINITION Oberon; (*system manager*)
-
- IMPORT Display, Viewers, Texts;
-
- CONST
-
- consume = 0; track = 1; (*ids for input messages*)
- defocus = 0; neutralize = 1; mark = 2; (*ids for control messages*)
-
- TYPE
-
- Painter = PROCEDURE (x, y: INTEGER);
- Marker = RECORD
- Fade, Draw: Painter
- END;
-
- Cursor = RECORD
- on: BOOLEAN;
- m: Marker;
- X, Y: INTEGER
- END;
-
- ParList = POINTER TO ParRec;
-
- ParRec = RECORD
- vwr: Viewers.Viewer; (*caller's viewer*)
- frame: Display.Frame; (*caller's sub-frame*)
- text: Texts.Text; (*parameter list*)
- pos: LONGINT (*starting position of parameter list*)
- END;
-
- InputMsg = RECORD
- (Display.FrameMsg)
- id: INTEGER; (*message id*)
- modes, keys: SET; (*current modes and mouse keys*)
- X, Y: INTEGER; (*current location of the mouse*)
- ch: CHAR (*current char*)
- END;
-
- ControlMsg = RECORD
- (Display.FrameMsg)
- id: INTEGER; (*message id*)
- X, Y: INTEGER (*current location of the mous*)
- END;
-
- SelectionMsg = RECORD
- (Display.FrameMsg)
- time: LONGINT;
- text: Texts.Text;
- beg, end: LONGINT
- END;
-
- CopyOverMsg = RECORD
- (Display.FrameMsg)
- text: Texts.Text;
- beg, end: LONGINT
- END;
-
- CopyMsg = RECORD
- (Display.FrameMsg)
- F: Display.Frame
- END;
-
- Task = POINTER TO TaskDesc; (*installable task*)
-
- Handler = PROCEDURE;
-
- TaskDesc = RECORD
- safe: BOOLEAN; (*safe tasks are not removed after trap*)
- handle: Handler
- END;
-
- VAR
-
- (*configuration*)
-
- FocusViewer: Viewers.Viewer; (*current focus viewer*)
- Log: Texts.Text; (*system log text*)
- Par: ParList; (*actual parameters for next command*)
- User: ARRAY 8 OF CHAR; Password: LONGINT; (*current user*)
-
- CurFnt, CurCol:, CurOff SHORTINT; (*current font, color, vertical offset*)
-
- Arrow, Star: Marker;
- Mouse, Pointer: Cursor;
-
- (*user identification*)
-
- PROCEDURE SetUser (VAR user, password: ARRAY OF CHAR);
-
- (*clocks*)
-
- PROCEDURE GetClock (VAR t, d: LONGINT);
- PROCEDURE SetClock (t, d: LONGINT);
- PROCEDURE Time (): LONGINT; (*in units of 1/300 sec*)
-
- (*cursor handling*)
-
- PROCEDURE OpenCursor (VAR c: Cursor);
- PROCEDURE FadeCursor (VAR c: Cursor);
- PROCEDURE DrawCursor (VAR c: Cursor; VAR m: Marker; X, Y: INTEGER);
-
- (*display management*)
-
- PROCEDURE OpenDisplay (UW, SW, H: INTEGER);
- (*initialize new display with user track width UW, system track width
- SW, and height H*)
-
- PROCEDURE DisplayWidth (X: INTEGER): INTEGER;
- (*get width of display at X*)
-
- PROCEDURE DisplayHeight (X: INTEGER): INTEGER;
- (*get height of display at X*)
-
- PROCEDURE OpenTrack (X, W: INTEGER);
- (*open a new track of width W at X*)
-
- PROCEDURE UserTrack (X: INTEGER): INTEGER;
- (*get left margin of user track at X*)
-
- PROCEDURE SystemTrack (X: INTEGER): INTEGER;
- (*get left margin of system track at X*)
-
- PROCEDURE AllocateUserViewer (DX: INTEGER; VAR X, Y: INTEGER);
- (*allocate new user viewer within display at DX*)
-
- PROCEDURE AllocateSystemViewer (DX: INTEGER; VAR X, Y: INTEGER);
- (*allocate new system viewer within display at DX*)
-
- PROCEDURE PassFocus (V: Viewers.Viewer);
- (*pass focus to viewer V*)
-
- PROCEDURE RemoveMarks (X, Y, W, H: INTEGER);
- (*remove marks within given rectangle*)
-
- PROCEDURE MarkedViewer (): Viewers.Viewer;
- (*returns viewer marked by star-shaped pointer*)
-
- (*command interpretation*)
-
- PROCEDURE ShowMenu (VAR cmd: INTEGER;
- X, Y: INTEGER;
- menu: ARRAY OF CHAR);
- (* menu = {command "|"} command.
- Six commands allowed, 6 > cmd >= -1.
- cmd = 5: first command selected
- cmd = 0: last command selected
- cmd = -1: no selection *)
-
- PROCEDURE Call (VAR name: ARRAY OF CHAR;
- par: ParList;
- new: BOOLEAN;
- VAR res: INTEGER);
- (*call command name and pass parameter list par. Option new requests
- loading of module. Done = (res = 0)*)
-
- PROCEDURE GetSelection (VAR text: Texts.Text;
- VAR beg, end, time: LONGINT);
- (*get most recent text selection. Text selection exists = (time >= 0)*)
-
-
- PROCEDURE Install (T: Task);
- (*install new task T*)
-
- PROCEDURE Remove (T: Task);
- (*remove installed task T*)
-
- PROCEDURE Collect;
- (*demand garbage collector*)
-
- PROCEDURE SetFont* (fnt: Fonts.Font);
- (*set current font*)
-
- PROCEDURE SetColor* (col: SHORTINT);
- (*set current color*)
-
- PROCEDURE SetOffset* (voff: SHORTINT);
- (*set current vertical offset*)
-
- END Oberon.
-
-
- Remark;
-
- Installed tasks are considered to be background activities. They are activated
- by the central loop when no input events have been detected. For example, the
- garbage collector is implemented as an installed task. Notice that installed
- tasks may be invalidated after their host module has been unloaded (or replaced).
- Unsafe tasks are automatically removed after a system trap in order to avoid
- an infinite repetition of the same error.
-
- --------------------------------------------------------------------------
-
-
- Tutorial Examples
-
-
- Write time stamp to system log
-
- PROCEDURE TimeStamp;
- BEGIN
- Texts.WriteString(W, "TimeStamp ");
- Texts.WriteInt(W, Oberon.Time(), 1);
- Texts.WriteLn(W);
- Texts.Append(Oberon.Log, W.buf)
- END TimeStamp;
-
- where
-
- VAR
- W: Texts.Writer;
-
- is globally defined initialized by Texts.OpenWriter(W).
-
- Remarks:
-
- 1. Normally, one (global) writer per module is sufficient.
-
- 2. If you desire a specific part of the output text to appear in a new font,
- for example in italics variant Syntax10i.Scn.Fnt, call
- Texts.SetFont(W,Fonts.This("Syntax10i.Scn.Fnt")) before writing this part
- and Texts.SetFont(W,Fonts.Default) before continuing to write ordinary text.
-
-
- Process selected text
-
- PROCEDURE CountWords;
- VAR
- T: Texts.Text;
- R: Texts.Reader;
- beg, end, pos, time: LONGINT;
- words: INTEGER; ch: CHAR;
- BEGIN
- words := 0;
- Oberon.GetSelection(T, beg, end, time); (*get most recent selection*)
- IF time >= 0 THEN (*if it exists*)
- Texts.OpenReader(R, T, beg);
- pos := beg; (*setup reader and initialize pos*)
- Texts.Read(R, ch);
- INC(pos); (*read next character*)
- IF (pos # end) & (ch > " ") THEN
- REPEAT
- Texts.Read(R, ch);
- INC(pos)
- UNTIL (pos = end) OR (ch <= " ");
- INC(words)
- END;
- WHILE pos # end DO
- (*(pos # end) & (ch <= " ")*)
- REPEAT
- Texts.Read(R, ch);
- INC(pos)
- UNTIL (pos = end) OR (ch > " ");
- IF pos # end THEN
- REPEAT
- Texts.Read(R, ch);
- INC(pos)
- UNTIL (pos = end) OR (ch <= "");
- INC(words)
- END;
- END;
- END;
- Texts.WriteString(W, "WordCount = ");
- Texts.WriteInt(W, words, 1);
- Texts.WriteLn(W);
- Texts.Append(Oberon.Log, W.buf) (*append to system log*)
- END CountWords;
-
-
- where again
-
- VAR
- W: Texts.Writer;
-
- is globally defined and initialized by Texts.OpenWriter(W).
-
-
- Open a viewer in system track, generate, and display text data
-
- PROCEDURE Directory;
- VAR
- Menu, Main: TextFrames.Frame;
- T: Texts.Text; V: Viewers.Viewer;
- X, Y:INTEGER;
- BEGIN
- T := TextFrames.Text(""); (*generate new (and empty) text to be displayed
- in a frame*)
- Menu := TextFrames.NewMenu("Directory", StandardMenu); (*generate standard menu frame*)
- Main := TextFrames.NewText(T, 0); (*generate standard text frame*)
- Oberon.AllocateSystemViewer(Oberon.Par.vwr.X, X, Y);
- V := MenuViewers.New(Menu, Main, TextFrames.menuH, X, Y); (*open standard menu viewer*)
- TextFrames.Mark(Main, -1); (*setup vertical arrow mark*)
- Diskette.Enumerate(Lister); (*pass over Lister-procedure to enumerator*)
- Texts.Append(T, W.buf); (*append writer to T and display written text*)
- TextFrames.Mark(Main, 1) (*restore position mark*)
- END Directory;
-
-
- where
-
- CONST
- StandardMenu = "System.Close System.Copy System.Grow Edit.Search Edit.Store";
- VAR
- T: Texts.Text;
- W : Texts.Writer;
-
- are globally defined, W is globally initialized by Texts.OpenWriter(W), and
- Lister is an (upcalled) procedure displaying directory entries:
-
- PROCEDURE* Lister (name: ARRAY OF CHAR; date, time, size: LONGINT);
- BEGIN
- Texts.WriteString(W, name);
- Texts.Write(W, " ");
- Texts.WriteInt(W, size, 1);
- Texts.Write(W, " ");
- Texts.WriteDate(W, time, date);
- Texts.WriteLn(W)
- END Lister;
-
-
- Remarks:
-
- 1. The above program generates its whole output text before displaying it.
- Alternatively, if you move the statement Texts.Append(T, W.buf) into the
- Lister-procedure, every generated directory entry is displayed immediately.
-
- 2. Oberon.AllocateSystemViewer(Oberon.Par.vwr.X, X, Y) is a standard proposal
- for the placing of a new system viewer within the track from which the
- command was called. Of course, individual algorithms are possible as well.
- For example, if the new viewer is desired to cover the bottom most viewer,
- except if the pointer overrides this, the algorithm is
-
- PROCEDURE AllocateSystemViewer (DX: INTEGER; VAR X, Y: INTEGER);
- VAR
- bot: Viewers.Viewer;
- BEGIN
- IF Oberon.Pointer.on THEN
- X := Oberon.Pointer.X; Y := Oberon.Pointer.Y
- ELSE
- bot := Viewers.This(Oberon.SystemTrack(DX), 0);
- X := bot.X;
- Y := bot.H - Viewers.minH
- END
- END AllocateSystemViewer;
-
- 3. TextFrames.NewText generates a standard text frame. The following statement
- sequence produce a text frame with an individual handler and a customized
- geometry.
-
- NEW(F); Open(F, Handle, text, pos, col, left, right, top, bot, asr, dsr,lsp);
-
- where F is of type TextFrames.Frame.
-
- Open a viewer in user track and display existing text
-
- PROCEDURE OpenText;
- VAR
- par: Oberon.ParList;
- Text: TextFrames.Frame;
- S: Texts.Scanner;
- V: Viewers.Viewer;
- X, Y: INTEGER;
- BEGIN
- par := Oberon.Par; (*access parameters*)
- Text := par.frame(TextFrames.Frame); (*calling frame*)
- TextFrames.Mark(Text, -1); (*arrow mark*)
- Texts.OpenScanner(S, par.text, par.pos); (*open scanner at position of parameter list*)
- Texts.Scan(S); (*get symbol*)
- IF S.class = Texts.Name THEN
- Oberon.AllocateUserViewer(par.vwr.X, X, Y);
- V := MenuViewers.New(TextFrames.NewMenu(S.s, StandardMenu);
- TextFrames.NewText(TextFrames.Text(S.s), 0);
- TextFrames.menuH, X, Y);
- END;
- TextFrames.Mark(Text, 1) (*restore position mark*)
- END OpenText;
-
-
- Remark:
-
- Oberon.AllocateUserViewer(par.vwr.X, X, Y) is a standard proposal for the
- placing of a new viewer in the caller's user track. Again, individual
- algorithms are possible as well.
-
-
- Grow viewer
-
- PROCEDURE Grow;
- VAR
- V, newV: Viewers.Viewer;
- M: Oberon.CopyMsg;
- N: Viewers.ViewerMsg;
- DH: INTEGER;
- BEGIN
- V := Oberon.Par.vwr; (*get originator viewer*)
- DH := Oberon.DisplayHeight(V.X); (*get height of this track*)
- IF V.H < Oberon.DisplayHeight(V.X) THEN (*if viewer is small*)
- Oberon.OpenTrack(V.X, V.W); (*open overlaying track*)
- V.handle(V, M); newV := M.F(Viewers.Viewer); (*get a copy of the viewer*)
- Viewers.Open(newV, V.X, DH); (*open new big viewer*)
- N.id := Viewers.restore; newV.handle(newV, N) (*ask new viewer to draw itself*)
- END
- END Grow;
-
-
- Remark:
-
- The Grow command is generic in the sense that it can handle viewer instances
- of any (current or future) class. Typically (and unavoidably) generic commands
- use message passing instead of ordinary procedurecalls. This object-oriented
- style will be explained in more detail in the next chapter. Also notice that
- actually a copy of the original viewer is opened in the new track. When this
- track is being closed later, the original viewer will reappear.
-
-
- Process viewer text or sequence of texts, depending on context
-
- PROCEDURE ProcessText;
- VAR
- par: Oberon.ParList;
- Main: TextFrames.Frame;
- S: Texts.Scanner;
- T: Texts.Text;
- BEGIN
- par := Oberon.Par; (*access parameters*)
- IF par.frame = par.vwr.dsc THEN (*command in menu frame*)
- IF par.vwr.dsc.next IS TextFrames.Frame THEN
- Main := par.vwr.dsc.next(TextFrames.Frame); (*main text frame*)
- TextFrames.Mark(Main, -1) (*set arrow mark*)
- Process(Main.text); (*process displayed text*)
- TextFrames.Mark(Main, 1) (*restore position mark*)
- END
- ELSE (*command in main text frame*)
- Main := par.frame(TextFrames.Frame);
- TextFrames.Mark(Main, -1) (*set arrow mark*)
- Texts.OpenScanner(S, par.text, par.pos); (*open scanner at position of parameter list*)
- Texts.Scan(S); (*get first symbol*)
- WHILE S.class = Texts.Name DO
- Texts.Open(T, S.s); (*open text from file*)
- Process(T); (*process this text*)
- Texts.Scan(S) (*get next symbol*)
- END;
- TextFrames.Mark(Main, 1) (*restore position mark*)
- END
- END ProcessText;
-
-
- Delete selected part of text in marked viewer
-
- PROCEDURE Delete;
- VAR
- Main: TextFrames.Frame;
- V: Viewers.Viewer;
- BEGIN
- V := Oberon.MarkedViewer(); (*get marked viewer*)
- Main := V.dsc.next(TextFrames.Frame); (*main text frame of marked viewer*)
- IF Main.sel > 0 THEN (*if there exists a selection*)
- Texts.Delete(Main.text, Main.selbeg.pos, Main.selend.pos) (*delete text*)
- END
- END Delete;
-
-
- Copy most recently selected text part to caret's position
-
- PROCEDURE CopyText;
- VAR
- Main: TextFrames.Frame;
- buf: Texts.Buffer;
- V: Viewers.Viewer; time: LONGINT;
- BEGIN
- Oberon.GetSelection(T, beg, end, time); (*get most recent selection*)
- IF time >= 0 THEN (*if it exists*)
- Texts.OpenBuffer(buf);
- Texts.Save(T, beg, end, buf); (*save text in buffer*)
- V := Oberon.FocusViewer; (*get focus viewer*)
- IF (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN (*if text viewer*)
- Main := V.dsc.next(TextFrames.Frame); (*main text frame*)
- IF Main.car > 0 THEN (*if caret set*)
- Texts.Insert(Main.text, Main.carloc.pos, buf) (*insert text at caret's position*)
- END
- END
- END
- END CopyText;
-
-
- Copy font from visibly marked position to text selection
-
- PROCEDURE CopyFont;
- VAR
- F: TextFrames.Frame;
- T: Texts.Text;
- R: Texts.Reader;
- V: Viewers.Viewer;
- beg, end, time: LONGINT;
- X, Y: INTEGER; ch: CHAR;
- BEGIN
- Oberon.GetSelection(T, beg, end, time); (*get most recent selection*)
- IF (time >= 0) & Oberon.Pointer.on THEN (*if found and pointer visible*)
- X := Oberons.Pointer.X; Y := Oberon.Pointer.Y;
- V := Viewers.This(X, Y); (*marked viewer*)
- IF (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN
- F := V.dsc.next(TextFrames.Frame);
- IF (X >= F.X) & (X < F.X + F.W) & (Y >= F.Y) & (Y < F.Y + F.H) THEN
- Texts.OpenReader(R, F.text, TextFrames.Pos(F, X, Y)); (*position reader*)
- Texts.Read(R, ch); (*read marked char*)
- Texts.ChangeLooks(T, beg, end, {0}, R.fnt, 0, 0) (*change font alone*)
- END
- END
- END
- END CopyFont;
-
-
- Move caret to next character written in italics
-
- PROCEDURE SearchItalics;
- VAR
- Main: TextFrames.Frame;
- R: Texts.Reader; italic:
- Fonts.Font;
- V:Viewers.Viewer;
- pos: LONGINT; ch: CHAR;
- BEGIN
- italic := Fonts.This("Syntax10i.Scn.Fnt");
- V := Oberon.FocusViewer; (*get focus viewer*)
- IF (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN (*if text viewer*)
- Main := V.dsc.next(TextFrames.Frame); (*main text frame*)
- IF Main.car > 0 THEN (*if caret set*)
- Texts.OpenReader(R, Main.text, Main.carloc.pos); (*open reader at caret's position*)
- Texts.Read(R, ch);
- WHILE ~R.eot & (R.fnt # italic) DO
- Texts.Read(R, ch)
- END; (*read char stream*)
- IF ~R.eot THEN (*not end of text*)
- pos := Texts.Pos(R); (*reader's position*)
- TextFrames.RemoveSelection(Main); (*remove all marks*)
- TextFrames.RemoveCaret(Main);
- Oberon.RemoveMarks(Main.X, Main.Y, Main.W, Main.H);
- TextFrames.Show(Main, Max(0, pos - 200)); (*show text at pos*)
- TextFrames.SetCaret(Main, pos) (*set caret to new position*)
- END
- END
- END
- END SearchItalics;
-
-
- where Max is the maximum-function.
-
-
- Guide for Programmers of new Frame Classes and Viewer Types
-
-
- Frames as Active Objects
-
- Frames are the basic entities of Oberon's display system. They are
- more-or-less autonomous rectangular areas on the display screen and
- may be nested. In particular, the display screen is hierarchically
- organized like this: Display > tracks > viewers > data frames. Display,
- tracks, and viewers are entities to be managed by the viewer handler
- module Viewers. Because of Oberon's tiling approach, all of these
- frames are totally visible or totally invisible, i.e. there is no
- partial overlapping on this level. There exists a class of standard
- viewers called menu-viewers (and handled by module MenuViewers ).
- Every menu-viewer contains exactly two vertically adjacent data frames:
- A header frame (including title and menu) and a main data frame.
- The main frame of a menu-viewer can be regarded as a virtual display
- terminal representing the user interface to a certain application or
- task. It may itself be nested, i.e. contain further subframes.
- Because an individual command interpreter is associated with every frame,
- implementing a new class of data frames is a most powerful way of adding
- functionality to the Oberon system.
-
- We have intentionally used the term class. As a matter of fact, frames are
- implemented in Oberon as active objects in the sense of object-oriented
- programming. Calls of frame-specific handling procedures are made by
- system routines in the form of message transfers. In particular, the
- central Oberon loop typically initiates reactions on user-input actions
- by sending appropriate messages to the respective viewers. We emphasize
- that employing the object-oriented programming paradigm in connection with
- the handling of frames is necessary in order to enable the kernel to call
- command interpreters whose identity is unknown to it.
-
- Any handler that is associated with a certain viewer class, for example
- menu-viewers, is expected to handle messages from (at least) three different
- originators: From the viewer manager, from the document manager, and from
- the central loop. Messages from the viewer manager report on the state change
- of the viewer. For example, the viewer manager Viewers sends a message whenever
- a hidden viewer becomes visible (and must therefore restore its contents), or
- when the size of a viewer has changed because its lower neighbour was opened,
- changed, or closed. Messages from the document manager remind the viewer of
- updating its contents after an editing operation.
-
- Finally, messages from module Oberon notify the viewer of system-wide events,
- such as input events (for example, "mouse button i pressed at position X, Y")
- or an inquiry for the most recent text selection (regarded in Oberon as the
- standard input ). The interpreter-part handling input events should be regarded
- as an editor that is bound to the viewer class. In the case of viewers
- displaying text, this is a text editor, in the case of graphics, it is a
- graphics editor, and in the case of a viewer representing a mailbox it is
- an editor for electronic mail.
-
- A typical viewer handler (like the one associated with menu-viewers) processes
- viewer-oriented messages directly and delegates the handling of the more data-
- specific messages by passing them on to the viewer's subframes. Prime examples
- of viewer-oriented messages are requests to restore a viewer or change its size.
- Examples of data-specific messages are selecting an object or invoking a
- command by pointing to its name. Notice that new and finer-grained requests
- may evolve from processing of viewer-oriented message.s For example, a request
- to change the size of a menu-viewer is resolved in requests to change the size
- of one or both of its subframes. In summarizing, viewer handlers normally act
- both as mediators and originators of messages to be processed by the handlers
- of their subframes.
-
-
- The Canonical Decomposition of an Application
-
- Modularizing by separation of concerns is one of the most effective techniques
- applied to the design of large software systems. Our concept of a class of
- frames displaying data of a certain kind provides a perfect opportunity to
- demonstrate this technique. We can extract the following separate topics from
- the program implementing an interactive application: the tool package, the
- command interpreter, the display handler, and the data manager. The following
- tasks are assigned to these parts: Providing a collection of powerful and
- customized commands operating on the data of the given kind (tool package),
- reacting on input actions within the associated display frame (command
- interpreter), displaying the visible objects (display handler), and
- encapsulating the management of the data without any concern of how they
- are displayed (data manager). Typically, the data part comprises a set of
- editing operations. It maintains an internal data structure describing the
- current state of the data.
-
- It is natural to try to assign a separate module to each of these parts. We
- soon see that the command interpreter part and the display manager are
- preferably combined in one module because both need to operate on the same
- associated display frame. This leads to the following canonical module
- configurations in the casess of standard texts, graphics, pictures, and
- MyData, for example:
-
-
- Tool Package Edit Draw Paint MyTool Comm. Int. & Disp. Handler TextFrames
- GraphicFrames PictureFrames MyFrames Data Manager Texts Graphics Pictures
- MyData
-
- Notice that the same data manager can in principle be used by different
- display handlers and command interpreters. Data managers serve as links
- between different applications and thus enable an optimal integration.
- For example, if the graphic system is based on module Texts for the
- management of text captions, then text can freely be exchanged between
- frames of these classes: Graphic frames and text frames are integrated.
-
- 1
- Tutorial Example: Text Viewers
-
- So far, we have not explained yet how objects and messages are realized in the
- Oberon language. In contrast to typical object-oriented programming systems,
- the complete set of messages understood by an object need not be specified
- together with the definition of the object class. Instead, Oberon explores
- a more flexible dual approach: Messages are typically defined in sender
- modules. For example, messages of the first of the above mentioned kinds
- are defined in module Viewers, messages of the second kind are defined in
- the respective editor module ( TextFrames in the case of text), and messages
- of the third kind are defined in module Oberon which detects input events.
-
- Roughly speaking, the Oberon language supports subclassing (by the
- type extension facility), but messages and message handlers are not
- institutionalized in the form of a language construct. We have
- implemented the above outlined scheme by making use of procedure variables,
- and by applying Oberon's record extension facility to objects and to messages.
-
- We shall now exemplify this model with the help of standard text-viewers,
- i.e. menu-viewers whose subframes (menu and main) are text frames. The
- following exposition may serve as a tutorial and template for implementors
- of arbitrary frame classes or viewer types. We recall that the display data
- structure is a hierarchy of display frames. Restricting our explanations to
- text-viewers we have the following hierarchy of types:
-
-
- Viewers.Track MenuViewers.Viewer
- ^ ^
- Viewers.Viewer TextFrames.Frame
- ^ ^
-
- Display.Frame
-
- Module Display features the base types of frames and frame messages and the
- type of the message handler. The (empty) base message serves as a root in the
- (potentially unlimited) hierarchy of messages to be accepted by frames. Module
- Viewers provides the definition of type Viewer, which is an extension (variant)
- of type Display.Frame. Menu-viewers are a based on Viewers.Viewer an defined in
- module MenuViewers.
-
-
- In definition of Display:
-
- TYPE
- Frame = POINTER TO FrameDesc;
- FrameMsg = RECORD
- END;
- Handler = PROCEDURE (Frame, VAR FrameMsg);
- FrameDesc = RECORD
- dsc, next: Frame; (*son, brother*)
- X, Y, W, H: INTEGER;
- handle: Handler
- END;
-
- In the definition of Viewers:
-
- TYPE
- Viewer = POINTER TO ViewerDesc;
- ViewerDesc = RECORD
- (Display.FrameDesc)
- state: INTEGER
- END;
-
- In the definition of MenuViewers:
-
- TYPE
- Viewer = POINTER TO ViewerDesc;
- ViewerDesc = RECORD
- (Viewers.ViewerDesc)
- menuH: INTEGER
- END;
-
- The following are the declarations of messages that are expected to be handled
- by every viewer. Note that they are distributed over several modules rather
- than concentrated in one place (as it would be the case in an "ordinary"
- object-oriented model).
-
- In definition of Viewers:
-
- ViewerMsg = RECORD (Display.FrameMsg)
- id: INTEGER;
- X, Y, W, H: INTEGER; (*new rectangle*)
- state: INTEGER (*new state*)
- END;
- (*signals change of viewer state
- id = 0: restore viewer
- 1: modify size at bottom
- 2: suspend viewer*)
-
- In definition of TextFrames:
-
- UpdateMsg = RECORD (Display.FrameMsg)
- id: INTEGER; (*operation*)
- text: Texts.Text; (*edited text*)
- beg, end: LONGINT (*stretch*)
- END;
- (*signals change of contents
- id = 0: stretch [beg, end[ replaced
- 1: stretch [beg, end[ inserted
- 2: stretch [beg, end[ deleted*)
-
- In definition of Oberon:
-
- InputMsg = RECORD (Display.FrameMsg)
- id: INTEGER; (*operation*)
- modes, keys: SET; (*mouse*)
- X, Y: INTEGER; (*position*)
- ch: CHAR (*character read*)
- END;
- (*signals input event
- id = 0: track mouse
- 1: consume character*)
-
- ControlMsg = RECORD (Display.FrameMsg)
- id: INTEGER; (*operation*)
- X, Y: INTEGER (*current location of the mous*)
- END;
-
- (*signals control action
- id = 0: remove focus
- 1: remove all marks
- 2: : mark*)
-
- SelectionMsg = RECORD (Display.FrameMsg)
- time: LONGINT;
- text: Texts.Text;
- beg, end: LONGINT
- END;
- (*signals inquiry for most recent text selection*)
-
- CopyOverMsg = RECORD (Display.FrameMsg)
- text: Texts.Text;
- beg, end: LONGINT
- END;
- (*receive text stretch [beg, end[*)
-
- CopyMsg* = RECORD (Display.FrameMsg)
- F: Display.Frame
- END;
- (*request for the creation of a copy of a text frame*)
-
- We recall that high-level viewer managers like MenuViewers typically pass on
- most of the received messages to their subframes. In the course of (pre-)
- processing viewer-oriented requests, high-level viewer managers can also
- produce new messages. In the case of MenuViewers these are
-
- ModifyMsg* = RECORD (Display.FrameMsg)
- id: INTEGER; (*operation*)
- dY, Y, H: INTEGER (*vector dY, new Y and H*)
- END;
- (*vertically move and extend or reduce frame at bottom*)
- id = 0: extend frame
- id = 1: reduce frame*)
-
- dY represents a vertical translation vector showing upwards in the extend-case
- and showing downwards in the reduce-case. By definition, dY is always
- non-negative. Y and H specify the new position and height respectively.
- In summary, essentially the same messages have to be handled by a viewer and
- its subframes, except that some of the messages are processed or preprocessed
- by the ancestor viewer already which, in turn, may result in sending new and
- finer-grained messages to its subframes. A message is sent to a specific object
- simply by calling its installed handler. For example, F.handle(F, M)
- has the effect of sending message M to frame F. In case of text-frames, the
- installed handler is Handle. It is elaborated in module TextFrames. We now
- provide a tutorial sketch of this procedure. Notice that Handle makes
- extensive use of Oberon's type test (and type guard) facility in order to
- discriminate among the different message kinds.
-
-
- 1 PROCEDURE Handle (F: Display.Frame; VAR M: Display.FrameMsg);
- 2 VAR
- 3 F1: Frame;
- 4 BEGIN
- 5 WITH F: Frame DO
- 6 IF M IS Oberon.InputMsg THEN
- 7 WITH M: Oberon.InputMsg DO
- 8 IF M.id = Oberon.track THEN
- 9 Edit(F, M.X, M.Y, M.keys)
- 10 ELSIF
- 11 M.id = Oberon.consume THEN
- 12 IF F.car # 0 THEN
- 13 Write(F, M.ch, M.fnt, M.col, M.voff)
- 14 END
- 15 END
- 16 END
- 17 ELSIF M IS Oberon.ControlMsg THEN
- 18 WITH M: Oberon.ControlMsg DO
- 19 IF M.id = Oberon.defocus THEN
- 20 Defocus(F)
- 21 ELSIF M.id = Oberon.neutralize THEN
- 22 Neutralize(F)
- 23 END
- 24 END
- 25 ELSIF M IS Oberon.SelectionMsg THEN
- 26 WITH M: Oberon.SelectionMsg DO
- 27 GetSelection(F, M.text, M.beg, M.end, M.time)
- 28 END
- 29 ELSIF M IS Oberon.CopyOverMsg THEN
- 30 WITH M: Oberon.CopyOverMsg DO
- 31 CopyOver(F, M.text, M.beg, M.end)
- 32 END
- 33 ELSIF M IS Oberon.CopyMsg THEN
- 34 WITH M: Oberon.CopyMsg DO
- 35 Copy(F, F1); M.F := F1
- 36 END
- 37 ELSIF M IS MenuViewers.ModifyMsg THEN
- 38 WITH M: MenuViewers.ModifyMsg DO
- 39 Modify(F, M.id, M.dY, M.Y, M.H)
- 40 END
- 41 ELSIF M IS UpdateMsg THEN
- 42 WITH M: UpdateMsg DO
- 43 IF F.text = M.text THEN
- 44 Update(F, M)
- 45 END
- 46 END
- 47 END
- 48 END
- 49 END Handle;
-
-
- Explanations:
-
- 1 procedure of type Display.Handler
- 2 auxiliary variable for frame copy (line 25). Needed because M.F is base type
- 5 type guard; F must be a text frame
- 6 type test; is message an Oberon input message?
- 7 type guard
- 8 if message demands mouse tracking
- 11 if message demands consuming then consume a character
- 12 if caret is active in this text frame
- 17 type test; is message an Oberon control message?
- 18 type guard
- 19 if message demands removing the caret
- 21 if message demands removing caret and selection
- 25 type test; is message an Oberon selection inquiry?
- 27 if so, register own selection if it is newer than candidate
- 29 type test; is message a copy-over-message?
- 31 if so, copy text stretch to caret's location in this frame
- 33 type test; is message a copy-message?
- 35 if so, produce a copy of this frame (initialized to empty)
- 37 type test; is message a modify-message?
- 39 if so, modify size or position of this frame (see below)
- 41 type test; is message an update message?
- 43 if so, check if changed text is represented in this frame and then update
- frame
-
- We also provide the following refinement of the procedure TextFrames.Modify
- called in line 37 in TextFrames.Handle. It shows well how subframes of menu-
- viewers should react on a MenuViewers.ModifyMsg.
-
- PROCEDURE Modify (F: Frame; id, dY, Y, H: INTEGER);
- BEGIN
- Mark(F, 0);
- RemoveMarks(F); (*remove position-bar, caret, and selection*)
- IF id = MenuViewers.extend THEN
- IF dY > 0 THEN (*if frame is to be moved*)
- Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + dY, 0); F.Y := F.Y + dY (*move original block*)
- END;
- Extend(F, Y) (*extend moved frame at its bottom*)
- ELSIF id = MenuViewers.reduce THEN
- Reduce(F, Y + dY); (*reduce original frame at its bottom*)
- IF dY > 0 THEN (*if frame is to be moved*)
- Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, Y, 0); F.Y := Y (*move reduced block*)
- END
- END;
- IF F.H > 0 THEN
- Mark(F, 1)
- END (*restore position-bar*)
- END Modify;
-
- Remember that dY is always non-negative, and notice that the reduce-part of
- the procedure is the exact inverse of the extend-part.
-
- Notice by the way that the above handler is used for the handling of both
- data-frames and standard header-frames. The two variants of text frames
- differ only by their background color. This fact testifies for a streamlined
- system design and is an example of the "today en-vogue" notion of code reusing.
-
- The attentative and experienced reader may have noticed that the module
- TextFrames plays two very different roles. Its first role is that of a
- (late-bound) handler of messages sent to frame instances. The second role
- is that of a library of procedures operating on text frames. Examples are
- Edit, Write, CopyOverShow, SetCaret, and TrackWord. To the two roles of
- TextFrames correspond two different ways of treating text frames, namely
- as active objects individually reacting on messages, and as passive
- rectangles to be operated on conventionally by invoking procedures.
- The second way of treating frames might be of importance in cases
- where text frames are text boxes belonging to the contents of some
- more complex document.
-
- Module TextFrames offers yet another choice, this time to potential
- implementors of handlers of subclasses of text frames: Either they
- implement their own handler by just adding increments, and then
- refer to Handle, or they compose an individual handler from the
- elements. For example, a designer of MailFrames might apply the
- first strategy. He might just add a few mail-oriented methods
- catching the mail-oriented messages, and then delegate all
- further processing to TextFrames.Handle. In contrast, an implementor
- of a new user-interface to text frames might prefer strategy 2.
-
- We conclude this section by explaining briefly the flow of control after,
- for example, a character has been typed. We assume that the focus viewer
- is a text viewer and that the caret is set in its main data frame.
- First, the central loop Oberon detects the new character in the keyboard
- buffer. It then sends an input consume-message to the focus-viewer which,
- in turn, passes on this message to its main subframe.
- After that, the handler (command interpreter) of this subframe locates the
- caret within the underlying text and subsequently calls the Insert-procedure
- of the text data manager Texts. On that, Insert inserts the character in the
- text and (up-)calls the associated notifier which, in our case, is residing
- in TextFrames. The notifier then sends a broadcast-message of type
- TextFrames.UpdateMsg to all visible viewers. Every single of these viewers
- passes on this message to its subframes. If a subframe understands the
- message and finds itself involved in this update, it restores its contents
- by accessing the new data, again from the data manager Texts.
-
- We now present some typical examples of implementations.
-
- Tutorial Examples 1: Frame oriented operations
-
- Display text line within text frame
-
- PROCEDURE DisplayLine(F: Frame;
- L: Line;
- VAR R: Texts.Reader;
- X, Y: INTEGER;
- len: LONGINT);
- VAR
- pat: Display.Pattern;
- NX, dx, x, y, w, h: INTEGER;
- BEGIN
- NX := F.X + F.W;
- WHILE (nextCh # CarriageReturn) & (R.fnt # NIL) DO
- Display.GetChar(R.fnt.raster, nextCh, dx, x, y, w, h, pat);
- IF (X + x + w <= NX) & (h # 0) THEN
- Display.CopyPattern(R.col, pat, X + x, Y + y, 2)
- END;
- X := X + dx;
- INC(len); Texts.Read(R, nextCh)
- END;
- L.len := len + 1;
- L.wid := X + eolW - (F.X + F.margW);
- L.eot := R.fnt = NIL;
- Texts.Read(R, nextCh)
- END DisplayLine;
-
- where the types Line and Frame are defined as follows. Notice that Line is
- a private type and TextFrames.Frame is the public projection of type Frame.
-
- Line = POINTER TO LineDesc;
- LineDesc = RECORD
- len: LONGINT; (*number of characters in this line*)
- wid: INTEGER; (*width of line box*)
- eot: BOOLEAN; (*end of text flag*)
- next: Line (*next lower neighbour*)
- END;
-
- Location = RECORD
- org, pos: LONGINT; (*line origin, position*)
- dx, x, y: INTEGER; (*width and position of located character*)
- lin: Line (*associated line*)
- END;
-
- Frame = POINTER TO FrameDesc;
- FrameDesc = RECORD (Display.FrameDesc)
- text: Texts.Text; (*displayed text*)
- org: LONGINT; (*position in text of first displayed character*)
- col: INTEGER; (*background color*)
- lsp, asr, dsr: INTEGER; (*line spacing, ascender, descender*)
- left, right, top, bot: INTEGER; (*margins*)
- markH: INTEGER; (*margin width, position of mark*)
- time: LONGINT; (*time of latest selection*)
- mark, car, sel: INTEGER; (*state of mark, caret, selection*)
- carloc: Location; (*caret location*)
- selbeg, selend: Location (*locations of begin and end of selection*)
- trailer: Line (*pointer to the associated sequence of line descriptors*)
- END;
-
-
- Track caret
-
- PROCEDURE TrackCaret (F: Frame; X, Y: INTEGER; VAR keysum: SET);
- VAR
- loc: Location;
- keys: SET;
- BEGIN
- IF F.trailer.next # F.trailer THEN
- LocateChar(F, X - F.X, Y - F.Y, F.carloc);
- FlipCaret(F);
- keysum := {};
- LOOP
- Input.Mouse(keys, X, Y);
- IF keys = {} THEN
- EXIT
- END;
- keysum := keysum + keys;
- Oberon.DrawCursor(Oberon.Mouse, Oberon.Mouse.marker, X, Y);
- LocateChar(F, X - F.X, Y - F.Y, loc);
- IF loc.pos # F.carloc.pos THEN
- FlipCaret(F); F.carloc := loc;
- FlipCaret(F)
- END
- END;
- F.car := 1
- END
- END TrackCaret;
-
-
-
- where the following auxiliary procedures are used:
-
- PROCEDURE LocateChar (F: Frame; x, y: INTEGER; VAR loc: Location);
- VAR
- R: Texts.Reader;
- pat: Display.Pattern;
- pos, lim: LONGINT;
- ox, dx, u, v, w, h: INTEGER;
- BEGIN
- LocateLine(F, y, loc);
- lim := loc.org + loc.lin.len - 1;
- pos := loc.org;
- ox := F.left;
- Texts.OpenReader(R, F.text, loc.org);
- Texts.Read(R, nextCh);
- LOOP
- IF pos = lim THEN
- dx := eolW;
- EXIT
- END;
- Display.GetChar(R.fnt.raster, nextCh, dx, u, v, w, h, pat);
- IF ox + dx > x THEN
- EXIT
- END;
- INC(pos);
- ox := ox + dx;
- Texts.Read(R, nextCh)
- END;
- loc.pos := pos;
- loc.dx := dx;
- loc.x := ox
- END LocateChar;
-
-
-
- PROCEDURE LocateLine (F: Frame; y: INTEGER; VAR loc: Location);
- VAR
- T: Texts.Text;
- L: Line;
- org: LONGINT;
- cury: INTEGER;
- BEGIN
- T := F.text;
- org := F.org;
- L := F.trailer.next;
- cury := F.H - F.top - F.asr;
- WHILE (L.next # F.trailer) & (cury > y + F.dsr) DO
- org := org + L.len;
- L := L.next;
- cury := cury - F.lsp
- END;
- loc.org := org;
- loc.lin := L;
- loc.y := cury
- END LocateLine;
-
-
-
- PROCEDURE FlipCaret (F: Frame);
- BEGIN
- IF F.carloc.x < F.W THEN
- IF (F.carloc.y >= 10) & (F.carloc.x + 12 < F.W) THEN
- Display.CopyPattern(white, BigCaret, F.X + F.carloc.x, F.Y + F.carloc.y - 10, 2)
- ELSIF
- (F.carloc.y >= 4) & (F.carloc.x + 6 < F.W) THEN
- Display.CopyPattern(white, SmallCaret, F.X + F.carloc.x, F.Y + F.carloc.y - 4, 2)
- END
- END
- END FlipCaret;
-
-
-
- Tutorial Examples 2: Text oriented operations
-
-
- Save text in buffer
-
- PROCEDURE Save (T: Text; beg, end: LONGINT; B: Buffer);
- VAR
- p, q, qb, qe: Piece;
- org: LONGINT;
- BEGIN
- IF end > T.len THEN
- end := T.len
- END;
- FindPiece(T, beg, org, p);
- NEW(qb); qb^ := p^;
- qb.len := qb.len - (beg - org);
- qb.off := qb.off + (beg - org);
- qe := qb;
- WHILE end > org + p.len DO
- org := org + p.len;
- p := p.next;
- NEW(q); q^ := p^;
- qe.next := q;
- q.prev := qe;
- qe := q
- END;
- qe.next := NIL;
- qe.len := qe.len - (org + p.len - end);
- B.last.next := qb;
- qb.prev := B.last;
- B.last := qe;
- B.len := B.len + (end - beg)
- END Save;
-
- where FindPiece is the following auxiliary procedure:
-
- PROCEDURE FindPiece (T: Text; pos: LONGINT; VAR org: LONGINT; VAR p: Piece);
- VAR
- n: INTEGER;
- BEGIN
- IF pos < T.org THEN
- T.org := -1;
- T.pce := T.trailer
- END;
- org := T.org;
- p := T.pce; (*from cache*)
- n := 0;
- WHILE pos >= org + p.len DO
- org := org + p.len;
- p := p.next;
- INC(n)
- END;
- IF n > 50 THEN
- T.org := org; T.pce := p
- END (*to cache*)
- END FindPiece;
-
- and where the types Piece, Text, and Buffer are defined as follows.
- Notice that Piece is a private type, Texts.Text is the public projection
- of type Text, and Texts.Buffer is the public part of type Buffer.
-
- Piece = POINTER TO PieceDesc;
- PieceDesc = RECORD
- f: Files.File; (*carrier file*)
- off: LONGINT; (*offset in file*)
- len: LONGINT; (*piece length*)
- fnt: Fonts.Font; (*font*)
- col: SHORTINT; (*color*)
- voff: SHORTINT; (*vertical offset*)
- prev, next: Piece (*links to neighbours*)
- END;
-
- Text = POINTER TO TextDesc;
- Notifier = PROCEDURE (Text, INTEGER, LONGINT, LONGINT);
- TextDesc = RECORD
- len: LONGINT; (*text length*)
- notify: Notifier; (*called after text changes*)
- trailer: Piece; (*sentinel*)
- org: LONGINT; (*cached origin*)
- pce: Piece (*cached piece*)
- END;
-
- Buffer = POINTER TO BufDesc;
- BufDesc = RECORD
- len: LONGINT; (*buffer length*)
- header, last: Piece (*buffered piece list*)
- END;
-
-
- Insert contents of buffer in text
-
- PROCEDURE Insert (T: Text; pos: LONGINT; B: Buffer);
- VAR
- pl, pr, p, qb, qe: Piece;
- org: LONGINT;
- BEGIN
- FindPiece(T, pos, org, p);
- SplitPiece(p, pos - org, pr);
- IF T.org >= org THEN (*adjust cache*)
- T.org := org - p.prev.len;
- T.pce := p.prev
- END;
- pl := pr.prev;
- qb := B.header.next;
- IF (qb # NIL) & (qb.f = pl.f) & (qb.off = pl.off + pl.len) & (qb.fnt = pl.fnt) THEN
- pl.len := pl.len + qb.len;
- qb := qb.next
- END;
- IF qb # NIL THEN
- qe := B.last;
- qb.prev := pl;
- pl.next := qb;
- qe.next := pr;
- pr.prev := qe
- END;
- T.len := T.len + B.len;
- T.notify(T, insert, pos, pos + B.len); (*call postprocessor*)
- B.last := B.header;
- B.last.next := NIL;
- B.len := 0
- END Insert;
-
- where SplitPiece is
-
- PROCEDURE SplitPiece (p: Piece; off: LONGINT; VAR pr: Piece);
- VAR
- q: Piece;
- BEGIN
- IF off > 0 THEN
- NEW(q);
- q.col := p.col;
- q.fnt := p.fnt;
- q.len := p.len - off;
- q.f := p.f;
- q.off := p.off + off;
- p.len := off;
- q.next := p.next;
- p.next := q;
- q.prev := p;
- q.next.prev := q;
- pr := q
- ELSE
- pr := p
- END
- END SplitPiece;
-
-
- Literature
-
- Ceres Workstation
-
- H. Eberle. Development and Analysis of a Workstation Computer.
- Diss. ETH No. 8431, 1987.
-
- B. Heeb. Design of the Processor-Board for the Ceres-2 Workstation,
- Bericht 93, Inst. f ur Informatik, ETH Z urich, November 1988.
-
-
- Oberon Language
-
- N. Wirth. Type Extensions.
- ACM Trans. on Prog. Languages and Systems, 10, 2 (April 1988), 204-214.
-
- N. Wirth. From Modula to Oberon.
- Software - Practice and Experience, 18, 7, (July 1988), 661-670.
-
- N. Wirth. The Programming Language Oberon.
- Software - Practice and Experience, 18, 7, (July 1988), 671- 690.
-
- J. Gutknecht. Variations on the Role of Module Interfaces.
- Structured Programming, 10, 1, (Jan. 1989), 40-46.
-
-
- Oberon System
-
- N. Wirth. An extensible system and a programming tool for workstation
- computers. Proc. IVth South African Computer Science Symposium.
- Pretoria, SouthAfrica.
-
- N. Wirth. Oberon: An Extensible Operating System for Workstations.
- Proc. Euromicro Conf., Z urich, 29.8. - 1.9.1988.
-
- N. Wirth and J. Gutknecht. The Oberon System.
- Bericht 88, Inst. f ur Informatik, ETH Z urich, July 1988,
- and Software - Practice and Experience, 19 (1989).
-
- N. Wirth. Designing a System from Scratch.
- Structured Programming, 10, 1 (Jan. 1989), 10-18.
-
-