home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / coders / jËzyki_programowania / oberon / text / oberonv1.guide.text (.txt) < prev    next >
Oberon Text  |  1977-12-31  |  85KB  |  1,417 lines

  1. Syntax10.Scn.Fnt
  2. ParcElems
  3. Alloc
  4. Syntax14.Scn.Fnt
  5. PopupElems
  6. Alloc
  7. OberonV4.Text
  8. Syntax10.Scn.Fnt
  9. Doc.Open OberonV4.Text
  10. Elem.Guide.Text
  11. Syntax10.Scn.Fnt
  12. Doc.Open Elem.Guide.Text
  13. Edit.Programming.Text
  14. Syntax10.Scn.Fnt
  15. Doc.Open Edit.Programming.Text
  16. Oberon.Guide.Text
  17. Syntax10.Scn.Fnt
  18. Doc.Open Oberon.Guide.Text
  19. Syntax20.Scn.Fnt
  20. Syntax12.Scn.Fnt
  21. Syntax16.Scn.Fnt
  22. Oberon.Guide.Text
  23. Syntax10.Scn.Fnt
  24. Doc.Open Oberon.Guide.Text
  25. Syntax12i.Scn.Fnt
  26. Oberon.Guide.Text
  27. Syntax10.Scn.Fnt
  28. Doc.Open Oberon.Guide.Text
  29. Syntax12b.Scn.Fnt
  30. This Text was written for the Oberon System V1, so it is very old. Please read 
  31.  and 
  32.  to see what has changed. The Chapter "User's Guide" is replaced by 
  33. The Oberon Guide
  34. System Release 1.2
  35. rg Gutknecht
  36. Abstract
  37. 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.
  38. Table of Contents
  39. Abstract
  40. Introduction
  41.    History
  42.    Design Principles
  43.    Acknowledgement
  44. User's Guide                (skiped, see 
  45.    Commands and Tools
  46.    The Edit Tool Package
  47.    The System Tool Package
  48.    The Compiler Tool Package
  49.    The Miscellaneous Tool Package
  50.    The Backup Tool Package
  51. Guide for Programmers of Commands
  52.    Oberon's module hierarchy
  53.    The Display System
  54.    The Text System
  55.    The Oberon Core
  56.    Tutorial Examples
  57.       Write time stamp to system log
  58.       Process selected text
  59.       Open viewer in system track, generate, and display text data
  60.       Open viewer in user track and display existing text
  61.       Grow text viewer
  62.       Process viewer text or sequence of texts, depending on context
  63.       Delete selected part of text in marked viewer
  64.       Copy most recently selected text part to caret's position
  65.       Copy font from visibly marked position to text selection
  66.       Move caret to next character written in italics
  67. Guide for Programmers of Frame Classes and Viewer Types
  68.    Frames as Active Objects
  69.    Standard Menu Viewers
  70.    The Canonical Decomposition of an Application
  71.    Tutorial Examples 1: Frame oriented operations
  72.       Display text line within text frame
  73.       Track caret
  74.    Tutorial Examples 2: Text oriented operations
  75.       Save text in buffer
  76.       Insert contents of buffer in text
  77. Literature
  78.    Ceres Workstation
  79.    Oberon Language
  80.    Oberon System
  81. Introduction
  82. History
  83. 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).
  84.     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.
  85. Design Principles
  86. 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.
  87.     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.
  88.     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.
  89.     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.
  90.     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.
  91.     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.
  92. Acknowledgement
  93. 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 M
  94. ssenb
  95. ck, and Beverly Sanders for their constructive critisism and valuable suggestions for improvements.
  96. User's Guide
  97. This chapter is skiped because 
  98.  contents the same text but updated for System V4.
  99. Guide for Programmers of Commands
  100. 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.
  101. Oberon's module hierarchy
  102. Tool Packages    Net   Backup   Compiler   System   Miscellaneous  ColorSystem
  103.         Edit    Draw    Paint
  104.         Text System    Graphic System    Picture System
  105.         TextFrames    GraphicFrames    PictureFrames
  106.                                                                      Graphics    Pictures
  107.                             MenuViewers
  108.                                                         Outer Core    
  109.                  Inner Core         Printer             Oberon
  110.         Texts
  111.     Modules    Fonts    
  112.     Files
  113.     FileDir                            Math    MathL   Reals    Viewers
  114.      Drivers    Kernel              V24    SCC     Diskette     Input        Display
  115. 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.
  116.     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 operations 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.
  117.     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. TextFrames 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.
  118.     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.
  119.     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.
  120. The Display System
  121. DEFINITION Display; (*display driver*)
  122.   CONST black = 0; white = 15;
  123.     replace = 0; paint = 1; invert = 2; (*operation modes*)
  124.    TYPE
  125.     Frame = POINTER TO FrameDesc;
  126.     FrameMsg = RECORD END; (*base type of messages to frames*)
  127.     Pattern = LONGINT; (*pointer to pattern descriptor*)
  128.     (*PatternDesc = RECORD
  129.         w, h: SHORTINT;
  130.        raster: ARRAY (w + 7) DIV 8 * h OF BYTE
  131.        END*)
  132.     Font = POINTER TO Bytes;
  133.     Bytes = RECORD END;
  134.     Handler = PROCEDURE (Frame, VAR FrameMsg);
  135.     FrameDesc = RECORD (*base type of frames*)
  136.         dsc, next: Frame;
  137.          X, Y, W, H: INTEGER;
  138.         handle: Handler
  139.       END;
  140.     VAR
  141.      Unit: LONGINT; (*RasterUnit = Unit/36000 mm*)
  142.       Left,            (*left margin of black-and-white maps*)
  143.       ColLeft,       (*left margin of color maps*)
  144.       Bottom,      (*Bottom of primary map*)
  145.       UBottom,   (*Bottom of secondary map*)
  146.       Width,       (*map width*)
  147.       Height:       (*map height*)
  148.           INTEGER;
  149.      arrow, star, cross, downArrow, hook: Pattern;
  150.     PROCEDURE Map (X: INTEGER): LONGINT; (*address of map at X*)
  151.     PROCEDURE SetMode (X: INTEGER; s: SET);  (*set mode of map at X*)
  152.       (*black & white display: 0: display disable, 1: display secondary map, 2: inverse video*)
  153.   (*color display*)
  154.     PROCEDURE SetColor (col, red, green, blue: INTEGER); (*col < 0: overlay color*)
  155.     PROCEDURE GetColor (col: INTEGER; VAR red, green, blue: INTEGER);
  156.     PROCEDURE SetCursor(mode: SET);  (*color cursor; 0: crosshair, 1: arrow*)
  157.     PROCEDURE InitCC;  (*initialize color crosshair to full screen*)
  158.     PROCEDURE InitCP;  (*initialize color pattern to arrow shape*)
  159.     PROCEDURE DefCC (X, Y, W, H: INTEGER);  (*define window for color crosshair*)
  160.     PROCEDURE DefCP (VAR raster: ARRAY OF BYTE); (*define 64 x 64 raster for color pattern marker*)
  161.     PROCEDURE DrawCX (X, Y: INTEGER);  (*draw color cursor at X, Y*)
  162.     PROCEDURE FadeCX (X, Y: INTEGER);  (*fade color cursor at X, Y*)
  163.   (*fonts*)
  164.     PROCEDURE GetChar(f: Font; ch: CHAR; VAR dx, x, y, w, h: INTEGER; VAR p: Pattern);
  165.       (*get box x, y, w, h, width dx, and raster data p of character ch in font f*)
  166.   (*raster operations*)
  167.     PROCEDURE CopyBlock (SX, SY, W, H, DX, DY, mode: INTEGER);
  168.       (*copy source block SX, SY, W, H to destination DX, DY using operation mode.
  169.       A block is given by its lower left corner X, Y and its dimension W, H*)
  170.     PROCEDURE CopyPattern (col: INTEGER; pat: Pattern; X, Y, mode: INTEGER);
  171.       (*copy pattern p in color col to X, Y using operation mode
  172.       col = 0: black; col = 15: white*)
  173.     PROCEDURE ReplPattern (col: INTEGER; pat: Pattern; X, Y, W, H, mode: INTEGER);
  174.       (*replicate pattern p in color col into block X, Y, W, H using operation mode,
  175.       proceeding from left to right and from bottom to top, starting at lower left corner*)
  176.     PROCEDURE ReplConst (col: INTEGER; X, Y, W, H, mode: INTEGER);
  177.       (*place "ones" in color col into block X, Y, W, H using operation mode*)
  178. END Display.
  179. Remarks:
  180. 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}.
  181. 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.
  182. 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.
  183. ------------------------------------------------------------------------
  184. DEFINITION Viewers; (*viewer manager*)
  185.   IMPORT Display;
  186.   CONST
  187.     restore = 0; modify = 1; suspend = 2;
  188.       (*message ids referring to the following message type*)
  189.   TYPE
  190.       Message = RECORD (*message sent to viewers on viewer events*)
  191.           (Display.FrameMsg)
  192.           id: INTEGER;
  193.           X, Y, W, H: INTEGER;
  194.           state: INTEGER
  195.        END;
  196.     Viewer = POINTER TO ViewerDesc;
  197.     ViewerDesc = RECORD (*viewer descriptor extends Display.FrameDesc*)
  198.         (Display.FrameDesc)
  199.          state: INTEGER
  200.       END;
  201.     (*state > 1: displayed
  202.        state = 1: filler
  203.        state = 0: closed
  204.       state < 0: suspended*)
  205.   VAR curW, minH: INTEGER; (*current width of logical display, minimum viewer height*)
  206.   PROCEDURE InitTrack (W, H: INTEGER; Filler: Viewer);
  207.     (*append to current logical display and init track of width W and height H and install Filler*)
  208.   PROCEDURE OpenTrack (X, W: INTEGER; Filler: Viewer);
  209.     (*open new track overlaying span of [X, X + W[*)
  210.   PROCEDURE CloseTrack (X: INTEGER);
  211.     (*close track at X and restore overlaid tracks*)
  212.   PROCEDURE Locate (X, H: INTEGER; VAR fil, bot, alt, max: Display.Frame);
  213.     (*in the track at X locate the following viewers:
  214.       filler fil,
  215.      bottom viewer bot,
  216.      an alternative viewer alt of height >= H,
  217.      viewer max of maximum height*)
  218.   PROCEDURE Open (V: Viewer; X, Y: INTEGER);
  219.     (*open new viewer V with top at Y in track at X*)
  220.   PROCEDURE Change (V: Viewer; Y: INTEGER);
  221.     (*expand or shrink viewer V to new top Y*)
  222.   PROCEDURE Close (V: Viewer);
  223.     (*remove viewer V from the display*)
  224.   PROCEDURE Recall (VAR V: Viewer);
  225.     (*recall most recently closed viewer*)
  226.   PROCEDURE This (X, Y: INTEGER): Viewer;
  227.     (*return viewer at X, Y*)
  228.   PROCEDURE Next (V: Viewer): Viewer;
  229.     (*return next upper neighbour of V*)
  230.   PROCEDURE Broadcast (VAR M: Display.FrameMsg);
  231.     (*send message M to all visible viewers*)
  232. END Viewers.
  233. --------------------------------------------------------------------------
  234. DEFINITION MenuViewers;
  235.   IMPORT Display, Viewers;
  236.   CONST extend = 0; reduce = 1; (*message ids*)
  237.   TYPE
  238.     Viewer = POINTER TO ViewerDesc;
  239.   ViewerDesc = RECORD (Viewers.ViewerDesc)
  240.       menuH: INTEGER (*height of menu frame*)
  241.     END;
  242.    ModifyMsg = RECORD (Display.FrameMsg)
  243.       id: INTEGER; (*extend or reduce*)
  244.       dY, Y, H: INTEGER (*translation vector dY; new Y and H*)
  245.     END;
  246.   VAR Ancestor: Viewer; (*current menu viewer*)
  247.   PROCEDURE Handle (V: Display.Frame; VAR M: Display.FrameMsg);
  248.       (*standard handler for menu viewers*)
  249.   PROCEDURE New (Menu, Main: Display.Frame; menuH, X, Y: INTEGER): Viewer;
  250.         (*create and open at X, Y new menu viewer containing frames Menu and Main*)
  251. END MenuViewers.
  252. Remark:
  253. 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.
  254. --------------------------------------------------------------------------
  255. The Text System
  256. DEFINITION Fonts; (*font loader*)
  257.   IMPORT Display;
  258.   TYPE
  259.       Name = ARRAY 32 OF CHAR;
  260.       Font = POINTER TO FontDesc;
  261.       FontDesc = RECORD
  262.          name: Name; (*file name*)
  263.          height, minX, maxX, minY, maxY: INTEGER; (*characteristic data*)
  264.          raster: Display.Font (*raster data*)
  265.       END;
  266.      (*height = minimum distance between text lines,
  267.       minX, maxX, minY, maxY are minima and maxima of X and Y,
  268.       if all character boxes of the font are placed at the origin 0, 0*)
  269.   VAR Default: Font; (*the default font*)
  270.       PROCEDURE This (name: ARRAY OF CHAR): Font;
  271.       (*font with name given*)
  272. END Fonts.
  273. --------------------------------------------------------------------------
  274. DEFINITION Texts; (*text manager*)
  275.   IMPORT Files, Fonts;
  276.   CONST
  277.     (*symbol classes, see def. of type Scanner*)
  278.     Inval = 0;          (*invalid symbol*)
  279.     Name = 1;        (*name s (length len)*)
  280.     String = 2;        (*literal string s (length len)*)
  281.     Int = 3;             (*integer i (decimal or hexadecimal)*)
  282.     Real = 4;          (*real number x*)
  283.     LongReal = 5;  (*long real number y*)
  284.     Char = 6;          (*special character c*)
  285.     replace = 0; insert = 1; delete = 2; (*op-codes*)
  286.   TYPE
  287.     Text = POINTER TO TextDesc;
  288.     Notifier = PROCEDURE (T: Text; op: INTEGER; beg, end: LONGINT);
  289.     TextDesc = RECORD
  290.       len: LONGINT; (*text length*)
  291.       notify: Notifier (*of editing operations*)
  292.     END;
  293.     Elem = POINTER TO ElemDesc;
  294.     ElemMsg = RECORD END;
  295.     Handler = PROCEDURE (e: Elem; VAR msg: ElemMsg);
  296.     ElemDesc = RECORD
  297.         W, H: LONGINT;
  298.         handle: Handler
  299.     END;
  300.     Reader = RECORD
  301.       (Files.Rider)
  302.       eot: BOOLEAN;
  303.       fnt: Fonts.Font; (*font of current character*)
  304.       col: SHORTINT; (*color of current character*)
  305.       voff: SHORTINT; (*vertical offset*)
  306.       elem: Elem
  307.     END;
  308.     Scanner = RECORD
  309.       (Reader)
  310.       nextCh: CHAR;
  311.       line: INTEGER;
  312.       class: INTEGER;
  313.       i: LONGINT;
  314.       x: REAL;
  315.       y: LONGREAL;
  316.       c: CHAR;
  317.       len: SHORTINT;
  318.       s: ARRAY 32 OF CHAR
  319.     END;
  320.      (*used to convert a text into a stream of symbols.
  321.      Symbol classes are defined under CONST*)
  322.     Buffer = POINTER TO BufDesc;
  323.     BufDesc = RECORD
  324.       len: LONGINT (*buffer length*)
  325.     END;
  326.     (*used to write a stream of textual data in a buffer*)
  327.     (*used to store a stretch of a text*)
  328.     Writer = RECORD
  329.       (Files.Rider)
  330.       buf: Buffer; (*associated buffer*)
  331.       fnt: Fonts.Font; (*current font*)
  332.       col: SHORTINT; (*color of current character*)
  333.       voff: SHORTINT (*vertical offset*)
  334.     END;
  335.     FileMsg = RECORD (ElemMsg)
  336.         id: INTEGER;
  337.         pos: LONGINT;
  338.         r: Files.Rider
  339.     END;
  340.     CopyMsg = RECORD (ElemMsg)
  341.         e: Elem
  342.     END;
  343.     IdentifyMsg = RECORD (ElemMsg)
  344.         mod, proc: ARRAY 32 OF CHAR
  345.     END;
  346.     new: Elem;
  347.   PROCEDURE Load (T: Text; f: Files.File; pos: LONGINT; VAR len: LONGINT);
  348.     (*load text block from file f at position pos to text T*)
  349.   PROCEDURE Open (T: Text; name: ARRAY OF CHAR);
  350.     (*open text T from disk file specified by name; open new text if name = ""*)
  351.   PROCEDURE OpenBuf (B: Buffer);
  352.     (*open new text buffer B*)
  353.   PROCEDURE OpenReader (VAR R: Reader; T: Text; pos: LONGINT);
  354.     (*open text reader R and set it up at position pos in text T*)
  355.   PROCEDURE Read (VAR R: Reader; VAR ch: CHAR);
  356.     (*read next character in ch*)
  357.   PROCEDURE Pos (VAR R: Reader): LONGINT;
  358.     (*return reader's position within its text*)
  359.   PROCEDURE Store (T: Text; f: Files.File; pos: LONGINT; VAR len: LONGINT);
  360.     (*store text T on disk file f at position pos*)
  361.   PROCEDURE Save (T: Text; beg, end: LONGINT; B: Buffer);
  362.     (*append stretch [beg, end[ of text T to buffer B*)
  363.   PROCEDURE Copy (SB, DB: Buffer);
  364.     (*append copy of source buffer SB to destination buffer DB*)
  365.   PROCEDURE ChangeLooks (T: Text; beg, end: LONGINT; sel: SET; fnt: Fonts.Font; col, voff: SHORTINT);
  366.     (*change character attributes within stretch [beg, end[ of text T. sel selects attributes to be changed.
  367.       0, 1, 2 IN sel = fnt, col, voff selected*)
  368.   PROCEDURE Insert (T: Text; pos: LONGINT; B: Buffer);
  369.     (*insert buffer B in text T at position pos*)
  370.   PROCEDURE Append (T: Text; B: Buffer);
  371.     (*append buffer B to text T*)
  372.   PROCEDURE Delete (T: Text; beg, end: LONGINT);
  373.     (*delete stretch [beg, end[ of text T*)
  374.   PROCEDURE Recall (VAR B: Buffer);
  375.     (*recall previously deleted text*)
  376.   PROCEDURE OpenScanner (VAR S: Scanner; T: Text; pos: LONGINT);
  377.     (*open text scanner S and set it up at position pos in text T*)
  378.   PROCEDURE Scan (VAR S: Scanner);
  379.     (*read next symbol*)
  380.   PROCEDURE OpenWriter (VAR W: Writer);
  381.     (*open new writer W*)
  382.   PROCEDURE SetFont (VAR W: Writer; fnt: Fonts.Font);
  383.     (*set writer W to font fnt*)
  384.   PROCEDURE SetColor (VAR W: Writer; col: SHORTINT);
  385.     (*set writer W to color col*)
  386.   PROCEDURE SetOffset (VAR W: Writer; voff: SHORTINT);
  387.     (*set writer W to vertical offset voff*)
  388.   PROCEDURE Write (VAR W: Writer; ch: CHAR);
  389.     (*write character ch to W's buffer*)
  390.   PROCEDURE WriteLn (VAR W: Writer);
  391.     (*write end-of-line to W's buffer*)
  392.   PROCEDURE WriteInt (VAR W: Writer; x, n: LONGINT);
  393.     (*write integer x to W's buffer. Right adjust to n positions*)
  394.   PROCEDURE WriteHex (VAR W: Writer; x: LONGINT);
  395.     (*write integer x to W's buffer in hexadecimal form.
  396.   PROCEDURE WriteString (VAR W: Writer; s: ARRAY OF CHAR);
  397.     (*write string s to W's buffer*)
  398.   PROCEDURE WriteReal (VAR W: Writer; x: REAL; n: INTEGER);
  399.     (*write real number x to W's buffer. Use n positions*)
  400.   PROCEDURE WriteRealFix (VAR W: Writer; x: REAL; n, k: INTEGER);
  401.     (*write real number x to W's buffer in fixed-point form,
  402.       using k positions for decimal fractions and n positions in total*)
  403.   PROCEDURE WriteRealHex (VAR W: Writer; x: REAL);
  404.     (*write real number x to W's buffer in hexadecimal form*)
  405.   PROCEDURE WriteLongReal (VAR W: Writer; x: LONGREAL; n: INTEGER);
  406.     (*write long real number x to W's buffer. Use n positions*)
  407.   PROCEDURE WriteLongRealHex (VAR W: Writer; x: LONGREAL);
  408.     (*write long real number x to W's buffer in hexadecimal form*)
  409.   PROCEDURE CopyElem (SE, DE: Elem);
  410.     (*copy all fields defined in the base type Elem*)
  411.   PROCEDURE ElemBase (E: Elem): Text;
  412.     (*text containing element*)
  413.   PROCEDURE ReadElem (VAR R: Reader);
  414.     (*read the next element at or after the current reader position*)
  415.   PROCEDURE ReadPrevElem (VAR R: Reader);
  416.     (*read the element before the reader position*)
  417.   PROCEDURE WriteElem (VAR W: Writer; e: Elem);
  418.     (*write element into writer buffer*)
  419. END Texts.
  420. Remark:
  421. 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.
  422. --------------------------------------------------------------------------
  423. DEFINITION TextFrames; (*text display*)
  424.   IMPORT Display, Texts;
  425.   TYPE
  426.     Location = RECORD
  427.       org, pos: LONGINT; (*line origin, position*)
  428.       dx, x, y: INTEGER (*width and position of located character*)
  429.     END;
  430.     Frame = POINTER TO FrameDesc;
  431.     FrameDesc = RECORD
  432.       (Display.FrameDesc)
  433.       text: Texts.Text; (*displayed text*)
  434.       org: LONGINT; (*position in text of first displayed character*)
  435.       col: INTEGER; (*background color*)
  436.       lsp, asr, dsr: INTEGER; (*line spacing, ascender, descender*)
  437.       left, right, top, bot: INTEGER; (*margins*)
  438.       markH: INTEGER; (*margin width, position of mark*)
  439.       time: LONGINT; (*time of latest selection*)
  440.       mark, car, sel: INTEGER; (*state of mark, caret, selection*)
  441.       carloc: Location; (*caret location*)
  442.       selbeg, selend: Location (*locations of begin and end of selection*)
  443.     END;
  444.     (*mark < 0: arrow mark
  445.       mark = 0: no mark
  446.       mark > 0: position mark
  447.       car = 0: caret not set
  448.       car > 0: caret set
  449.       sel = 0: no selection active
  450.       sel > 0: selection active*)
  451.      UpdateMsg* = RECORD
  452.         (Display.FrameMsg)
  453.          id: INTEGER;
  454.         text: Texts.Text;
  455.         beg, end: LONGINT
  456.       END;
  457.     VAR menuH, barW, left, right, top, bot, asr, dsr, lsp: INTEGER; (*standard sizes*)
  458.       PROCEDURE Restore (F: Frame);
  459.         (restore frame F*)
  460.      PROCEDURE Suspend(F: Frame);
  461.         (*suspend frame F*)
  462.      PROCEDURE Extend (F: Frame; newY: INTEGER);
  463.       (*extend frame F to bottom newY*)
  464.     PROCEDURE Reduce (F: Frame; newY: INTEGER);
  465.       (*reduce frame F to bottom newY*)
  466.     PROCEDURE Mark (F: Frame; mark: INTEGER);
  467.       (*mark frame F as specified by mark*)
  468.     PROCEDURE Show (F: Frame; pos: LONGINT);
  469.       (*show text part containing position pos in frame F*)
  470.     PROCEDURE Pos (F: Frame; X, Y: INTEGER): LONGINT;
  471.       (*convert coordinates X, Y to text position*)
  472.     PROCEDURE SetCaret (F: Frame; pos: LONGINT);
  473.       (*set caret in frame F at position pos*)
  474.     PROCEDURE TrackCaret (F: Frame; X, Y: INTEGER; VAR keysum: SET);
  475.       (*track caret in frame F, starting from X, Y, and return mouse-keys pressed*)
  476.     PROCEDURE RemoveCaret (F: Frame);
  477.       (*remove caret from frame F*)
  478.     PROCEDURE SetSelection (F: Frame; beg, end: LONGINT);
  479.       (*select text stretch [beg, end[ in F*)
  480.     PROCEDURE TrackSelection (F: Frame; X, Y: INTEGER; VAR keysum: SET);
  481.       (*track selection in frame F, starting from X, Y, and return mouse-keys pressed*)
  482.     PROCEDURE RemoveSelection (F: Frame);
  483.       (*remove selection from frame F*)
  484.     PROCEDURE TrackLine (F: Frame; X, Y: INTEGER; VAR org: LONGINT; VAR keysum: SET);
  485.       (*track text line in frame F, starting from X, Y, and return line-origin and mouse-keys pressed*)
  486.     PROCEDURE TrackWord (F: Frame; X, Y: INTEGER; VAR pos: LONGINT; VAR keysums: SET);
  487.       (*track text word in frame F, starting from X, Y,
  488.      and return starting position and mouse-keys pressed*)
  489.     PROCEDURE Replace (F: Frame; beg, end: LONGINT);
  490.       (*text stretch [beg, end[ was replaced; update frame F*)
  491.     PROCEDURE Insert (F: Frame; beg, end: LONGINT);
  492.       (*text stretch [beg, end[ was inserted; update frame F*)
  493.     PROCEDURE Delete (F: Frame; beg, end: LONGINT);
  494.       (*text stretch [beg, end[ was deleted; update frame F*)
  495.    (*---------------- message handling ----------------*)
  496.     PROCEDURE NotifyDisplay (T: Texts.Text; op: INTEGER; beg, end: LONGINT);
  497.       (*notify display manager of text status change*)
  498.     PROCEDURE Call* (F: Frame; pos: LONGINT; new: BOOLEAN);
  499.      (*call command specified at pos in frame F. new forces loading of newest version*)
  500.     PROCEDURE Write* (F: Frame; ch: CHAR; fnt: Fonts.Font; col, voff: SHORTINT);
  501.      (*write character ch with given attributes at caret position*)
  502.     PROCEDURE Defocus* (F: Frame); (F: Frame; ch: CHAR; fnt: Fonts.Font; col, voff: SHORTINT);
  503.       (*remove caret*)
  504.     PROCEDURE Neutralize* (F: Frame);
  505.       (*remove marks*)
  506.     PROCEDURE Modify* (F: Frame; id, dY, Y, H: INTEGER);
  507.      (*vertically translate and extend or reduce frame F. id indicates type (extension or reduction),
  508.        dy is a translation vector, and Y, H specify new location and height respectively*)
  509.     PROCEDURE Open* (
  510.         F: Frame; H: Display.Handler; T: Texts.Text; org: LONGINT;
  511.         col, left, right, top, bot, asr, dsr, lsp: INTEGER);
  512.       (*open new text frame F displaying text T starting from position org, with background color col,
  513.        margins left, right, top, bot, and line geometry asr, dsr, lsp = ascender, descender line spacing.
  514.            Install notifier H*)
  515.     PROCEDURE Copy* (F: Frame; VAR F1: Frame);
  516.       (*generate copy F1 of frame F. Initialize to empty frame*)
  517.     PROCEDURE CopyOver* (F: Frame; text: Texts.Text; beg, end: LONGINT);
  518.       (*copy over text stretch [beg, end[ to caret position in frame F*)
  519.     PROCEDURE GetSelection* (F: Frame; VAR text: Texts.Text; VAR beg, end, time: LONGINT);
  520.       (*get current text selection in frame F (if any)*)
  521.     PROCEDURE Update* (F: Frame; VAR M: UpdateMsg);
  522.       (*update display after editing operation*)
  523.     PROCEDURE Edit* (F: Frame; X, Y: INTEGER; Keys: SET);
  524.       (*track mouse and interpret editing commands*)
  525.     PROCEDURE Handle* (F: Display.Frame; VAR M: Display.FrameMsg);
  526.       (*standard handler for text frames*)
  527.     PROCEDURE Text* (name: ARRAY OF CHAR): Texts.Text;
  528.       (*create new displayed text from named file. Empty file name means empty text*)
  529.     PROCEDURE NewMenu* (name, commands: ARRAY OF CHAR): Frame;
  530.       (*create new menu frame containing listed commands*)
  531.     PROCEDURE NewText* (text: Texts.Text; pos: LONGINT): Frame;
  532.       (*create new standard text frame*)
  533. END TextFrames.
  534. --------------------------------------------------------------------------
  535. The Oberon Core
  536. DEFINITION Math; (*math library for reals*)
  537.   CONST pi = 3.14159265; e = 2.71828182;
  538.   PROCEDURE sqrt(x: REAL): REAL;
  539.   PROCEDURE exp(x: REAL): REAL;
  540.   PROCEDURE ln(x: REAL): REAL;
  541.   PROCEDURE sin(x: REAL): REAL;
  542.   PROCEDURE cos(x: REAL): REAL;
  543.   PROCEDURE arctan(x: REAL): REAL;
  544. END Math.
  545. --------------------------------------------------------------------------
  546. DEFINITION MathL; (*math library for longreals*)
  547.     CONST pi = 3.141592653589793D0;
  548.       e = 2.718281828459045D0;
  549.   PROCEDURE sqrt(x: LONGREAL): LONGREAL;
  550.   PROCEDURE exp(x: LONGREAL): LONGREAL;
  551.   PROCEDURE ln(x: LONGREAL): LONGREAL;
  552.   PROCEDURE sin(x: LONGREAL): LONGREAL;
  553.   PROCEDURE cos(x: LONGREAL): LONGREAL;
  554.   PROCEDURE arctan(x: LONGREAL): LONGREAL;
  555. END MathL.
  556. --------------------------------------------------------------------------
  557. DEFINITION Files; (*file manager*)
  558.   TYPE Handle = RECORD END ;
  559.        File   = POINTER TO Handle;
  560.      (*A file is a sequence of bytes, accessed via (a pointer to) a  handle. Files are stored on disk and
  561.        may be referenced through a name entered in the file directory*)
  562.        Rider  = RECORD
  563.                   res: INTEGER;
  564.                   eof: BOOLEAN;
  565.                   file: File
  566.                 END ;
  567.     (*Elements  of files are accessed through a rider, which has a position that is advanced when
  568.       reading or writing data. The position is an integer between 0 and the length of the file to which
  569.       the rider is attached. The fields eof and res serve as result parameters of file procedures.*)
  570.   PROCEDURE Old(name: ARRAY OF CHAR): File;
  571.     (*the file with the given name. NIL if the name is not in the directory*)
  572.   PROCEDURE New(name: ARRAY OF CHAR): File;
  573.     (*a new file with given name*)
  574.   PROCEDURE Register(f: File);
  575.     (*Close file f and register it under its name in the directory.
  576.       If the name exists already, the corresponding old file is unregistered*)
  577.   PROCEDURE Close(f: File);
  578.   PROCEDURE Purge(f: File);
  579.   PROCEDURE Length(f: File): LONGINT;  (*the number of bytes in the file*)
  580.   PROCEDURE Set(VAR r: Rider; f: File; pos: LONGINT);
  581.     (*Associate rider r with file f at position pos. r.eof := FALSE*)
  582.   PROCEDURE Read(VAR r: Rider; VAR x: BYTE);
  583.     (*read byte and advance rider by one position. If at end, r.eof := TRUE and x := 0X*)
  584.   PROCEDURE ReadBytes(VAR r: Rider; VAR x: ARRAY OF BYTE; n: INTEGER);
  585.     (*read n bytes and advance rider by n positions.
  586.       If at end, r.eof := TRUE and r.res := no. of bytes requested but not read.*)
  587.   PROCEDURE Write(VAR r: Rider; x: BYTE);
  588.     (*write byte and advance rider by one position*)
  589.   PROCEDURE WriteBytes(VAR r: Rider; VAR x: ARRAY OF BYTE; n: INTEGER);
  590.     (*write n bytes and advance rider by n positions*)
  591.   PROCEDURE Pos(VAR r: Rider): LONGINT;
  592.   PROCEDURE Base(VAR r: Rider): File;
  593.   PROCEDURE Rename(old, new: ARRAY OF CHAR; VAR res: INTEGER);
  594.     (*res = 0: renamed;  res = 1: new name existed already and now denotes the renamed file;
  595.         res = 2: old name not in directory;  res = 3: name is illegal;  res = 4: name is too long *)
  596.   PROCEDURE Delete(name: ARRAY OF CHAR; VAR res: INTEGER);
  597.     (*res = 0: deleted;  res = 2: name not in directory;
  598.         res = 3: name is illegal;  res = 4: name is too long *)
  599. END Files.
  600. --------------------------------------------------------------------------
  601. DEFINITION Diskette; (*diskette manager*)
  602.   TYPE EntryHandler* = PROCEDURE (name: ARRAY OF CHAR; date, time: INTEGER; size: LONGINT);
  603.   VAR res: INTEGER; (*result of file-oriented operation, error occurred = (res # 0)*)
  604.        err: SHORTINT; sect: LONGINT; busy: BOOLEAN; (*state of device driver*)
  605.   (*device driver*)
  606.   PROCEDURE Reset;
  607.   PROCEDURE GetSector (sec: INTEGER; VAR buf: ARRAY OF BYTE; off: INTEGER);
  608.   PROCEDURE PutSector (sec: INTEGER; VAR buf: ARRAY OF BYTE; off: INTEGER);
  609.   PROCEDURE Format;
  610.   (*directory handler*)
  611.   PROCEDURE InitDir (format: CHAR); (*format for future extension*)
  612.   PROCEDURE ReadDir;
  613.   PROCEDURE WriteDir;
  614.   PROCEDURE GetData (VAR date, time, nofFiles, nofClusters: INTEGER); (*get volume data*)
  615.   PROCEDURE Enumerate (proc: EntryHandler);
  616.   (*file handler*)
  617.   PROCEDURE ReadAll;
  618.   PROCEDURE ReadFile (name: ARRAY OF CHAR);
  619.   PROCEDURE WriteFile (name: ARRAY OF CHAR);
  620.   PROCEDURE DeleteFile (name: ARRAY OF CHAR);
  621. END Diskette.
  622. --------------------------------------------------------------------------
  623. DEFINITION Input; (*keyboard and mouse driver*)
  624.   PROCEDURE Available(): INTEGER;
  625.     (*the number of characters available from the keyboard*)
  626.   PROCEDURE Read (VAR ch: CHAR);
  627.     (*next character from keyboard*)
  628.   PROCEDURE Mouse (VAR keys: SET; VAR x, y: INTEGER);
  629.     (*current coordinates and key setting of mouse.
  630.       0 IN keys = right key pressed
  631.       1 IN keys = middle key pressed
  632.       2 IN keys = left key pressed*)
  633.   PROCEDURE SetMouseLimits (w, h: INTEGER);
  634.     (* define width and height of rectangle in which mouse moves*)
  635.   PROCEDURE Time(): LONGINT;
  636.     (* current system time in units of 1/300 sec*)
  637. END Input.
  638. --------------------------------------------------------------------------
  639. DEFINITION SCC; (*SCC driver*)
  640.   (*Serial Communications Controller driver module (Zilog Z8530)
  641.     Data are transmitted in blocks. Each block contains two parts: header and data *)
  642.   TYPE Header =
  643.     RECORD valid: BOOLEAN; dadr, sadr, typ: SHORTINT;
  644.       len: INTEGER; (*of data following header*)
  645.       destLink, srcLink: INTEGER  (*link numbers*)
  646.     END ;
  647.   (*dadr is the receiver's machine number, len is the length (number of bytes) of
  648.     the data part. typ, destLink, and srcLink are not interpreted by SCC*)
  649.   PROCEDURE Start(filter: BOOLEAN);
  650.     (*initialise the SCC*)
  651.   PROCEDURE Send(VAR head, buf: ARRAY OF BYTE);
  652.     (*send buf[0] ... buf[head.len-1] to head.adr*)
  653.   PROCEDURE Available(): INTEGER;
  654.     (*number of bytes available from receiver buffer. Buffer contains stream of
  655.       received bytes, including headers and data parts*)
  656.   PROCEDURE ReceiveHead(VAR head: ARRAY OF BYTE);
  657.     (*read a header from the receiver buffer*)
  658.   PROCEDURE Receive(VAR x: BYTE);
  659.     (*read a byte from the receiver buffer*)
  660.   PROCEDURE Skip(m: INTEGER);
  661.     (*skip m bytes in the receiver buffer*)
  662.   PROCEDURE Stop;  (*turn SCC off*)
  663. END SCC.
  664. --------------------------------------------------------------------------
  665. DEFINITION Printer; (*printer interface*)
  666.     VAR res: INTEGER; (*result*)
  667.        PROCEDURE Open(VAR name, user: ARRAY OF CHAR; password: LONGINT);
  668.         (*res = 0: opened, 1: no printer, 2: no link, 3: bad response, 4: no permission*)
  669.         PROCEDURE Circle (x0, y0, r: INTEGER); (*place circle*)
  670.         PROCEDURE ContString (VAR s, fname: ARRAY OF CHAR); (*place continuation string*)
  671.         PROCEDURE Ellipse (x0, y0, a, b: INTEGER); (*place ellipsis*)
  672.         PROCEDURE Line (x0, y0, x1, y1: INTEGER); (*place line of general direction*)
  673.         PROCEDURE Page (nofcopies: INTEGER); (*print current page*)
  674.         PROCEDURE Picture (x, y, w, h, mode: INTEGER; adr: LONGINT); (*place picture*)
  675.         PROCEDURE ReplConst (x, y, w, h: INTEGER); (*place horizontal or vertical line*)
  676.         PROCEDURE ReplPattern (x, y, w, h, col: INTEGER); (*shade area*)
  677.         PROCEDURE Spline (x0, y0, n, open: INTEGER; VAR X, Y: ARRAY OF INTEGER); (*place spline*)
  678.         PROCEDURE String (x, y: INTEGER; VAR s, fname: ARRAY OF CHAR); (*place string*)
  679.         PROCEDURE UseListFont (VAR name: ARRAY OF CHAR);
  680.         PROCEDURE Close; (*close connection*)
  681. END Printer.
  682. --------------------------------------------------------------------------
  683. DEFINITION Oberon; (*system manager*)
  684.   IMPORT Display, Viewers, Texts;
  685.   CONST
  686.     consume = 0; track = 1; (*ids for input messages*)
  687.     defocus = 0; neutralize = 1; mark = 2; (*ids for control messages*)
  688.   TYPE
  689.     Painter = PROCEDURE (x, y: INTEGER);
  690.     Marker = RECORD Fade, Draw: Painter END;
  691.     Cursor = RECORD
  692.        on: BOOLEAN; m: Marker; X, Y: INTEGER
  693.     END;
  694.    ParList = POINTER TO ParRec;
  695.    ParRec = RECORD
  696.       vwr: Viewers.Viewer; (*caller's viewer*)
  697.       frame: Display.Frame; (*caller's sub-frame*)
  698.       text: Texts.Text; (*parameter list*)
  699.       pos: LONGINT (*starting position of parameter list*)
  700.    END;
  701.     InputMsg = RECORD
  702.       (Display.FrameMsg)
  703.       id: INTEGER; (*message id*)
  704.       modes, keys: SET; (*current modes and mouse keys*)
  705.       X, Y: INTEGER; (*current location of the mouse*)
  706.       ch: CHAR (*current char*)
  707.     END;
  708.     ControlMsg = RECORD
  709.       (Display.FrameMsg)
  710.       id: INTEGER; (*message id*)
  711.       X, Y: INTEGER (*current location of the mous*)
  712.      END;
  713.     SelectionMsg = RECORD
  714.       (Display.FrameMsg)
  715.       time: LONGINT;
  716.       text: Texts.Text;
  717.       beg, end: LONGINT
  718.     END;
  719.      CopyOverMsg* = RECORD
  720.       (Display.FrameMsg)
  721.       text*: Texts.Text;
  722.       beg*, end*: LONGINT
  723.     END;
  724.     CopyMsg* = RECORD
  725.       (Display.FrameMsg)
  726.       F*: Display.Frame
  727.     END;
  728.    Task = POINTER TO TaskDesc; (*installable task*)
  729.     Handler = PROCEDURE;
  730.     TaskDesc = RECORD
  731.       safe: BOOLEAN; (*safe tasks are not removed after trap*)
  732.       handle: Handler
  733.     END;
  734.   VAR
  735.   (*configuration*)
  736.     FocusViewer: Viewers.Viewer; (*current focus viewer*)
  737.     Log: Texts.Text; (*system log text*)
  738.     Par: ParList; (*actual parameters for next command*)
  739.     User: ARRAY 8 OF CHAR; Password: LONGINT; (*current user*)
  740.     CurFnt, CurCol:, CurOff SHORTINT; (*current font, color, vertical offset*)
  741.      Arrow, Star: Marker;
  742.      Mouse, Pointer: Cursor;
  743.   (*user identification*)
  744.    PROCEDURE SetUser (VAR user, password: ARRAY OF CHAR);
  745.   (*clocks*)
  746.     PROCEDURE GetClock (VAR t, d: LONGINT);
  747.     PROCEDURE SetClock (t, d: LONGINT);
  748.     PROCEDURE Time (): LONGINT; (*in units of 1/300 sec*)
  749.   (*cursor handling*)
  750.     PROCEDURE OpenCursor (VAR c: Cursor);
  751.     PROCEDURE FadeCursor (VAR c: Cursor);
  752.     PROCEDURE DrawCursor (VAR c: Cursor; VAR m: Marker; X, Y: INTEGER);
  753.    (*display management*)
  754.     PROCEDURE OpenDisplay (UW, SW, H: INTEGER);
  755.         (*initialize new display with user track width UW, system track width SW, and height H*)
  756.     PROCEDURE DisplayWidth (X: INTEGER): INTEGER;
  757.         (*get width of display at X*)
  758.     PROCEDURE DisplayHeight (X: INTEGER): INTEGER;
  759.         (*get height of display at X*)
  760.      PROCEDURE OpenTrack (X, W: INTEGER);
  761.         (*open a new track of width W at X*)
  762.      PROCEDURE UserTrack (X: INTEGER): INTEGER;
  763.         (*get left margin of user track at X*)
  764.      PROCEDURE SystemTrack (X: INTEGER): INTEGER;
  765.         (*get left margin of system track at X*)
  766.      PROCEDURE AllocateUserViewer (DX: INTEGER; VAR X, Y: INTEGER);
  767.         (*allocate new user viewer within display at DX*)
  768.      PROCEDURE AllocateSystemViewer (DX: INTEGER; VAR X, Y: INTEGER);
  769.         (*allocate new system viewer within display at DX*)
  770.     PROCEDURE PassFocus (V: Viewers.Viewer);
  771.         (*pass focus to viewer V*)
  772.       PROCEDURE RemoveMarks (X, Y, W, H: INTEGER);
  773.         (*remove marks within given rectangle*)
  774.     PROCEDURE MarkedViewer (): Viewers.Viewer;
  775.         (*returns viewer marked by star-shaped pointer*)
  776.   (*command interpretation*)
  777.      PROCEDURE ShowMenu (VAR cmd: INTEGER; X, Y: INTEGER; menu: ARRAY OF CHAR);
  778.        (* menu = {command "|"} command.
  779.            Six commands allowed, 6 > cmd >= -1.
  780.            cmd  = 5:  first command selected
  781.            cmd = 0:  last command selected
  782.            cmd = -1:  no selection *)
  783.       PROCEDURE Call (VAR name: ARRAY OF CHAR; par: ParList; new: BOOLEAN; VAR res: INTEGER);
  784.         (*call command name and pass parameter list par. Option new requests loading of module.
  785.             Done = (res = 0)*)
  786.       PROCEDURE GetSelection (VAR text: Texts.Text; VAR beg, end, time: LONGINT);
  787.       (*get most recent text selection. Text selection exists = (time >= 0)*)
  788.         PROCEDURE Install (T: Task);
  789.          (*install new task T*)
  790.       PROCEDURE Remove (T: Task);
  791.         (*remove installed task T*)
  792.       PROCEDURE Collect;
  793.         (*demand garbage collector*)
  794.       PROCEDURE SetFont* (fnt: Fonts.Font);
  795.         (*set current font*)
  796.       PROCEDURE SetColor* (col: SHORTINT);
  797.         (*set current color*)
  798.       PROCEDURE SetOffset* (voff: SHORTINT);
  799.         (*set current vertical offset*)
  800. END Oberon.
  801. Remark:
  802. 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.
  803. --------------------------------------------------------------------------
  804. Tutorial Examples
  805. Write time stamp to system log
  806.   PROCEDURE TimeStamp;
  807.   BEGIN
  808.     Texts.WriteString(W, "TimeStamp "); Texts.WriteInt(W, Oberon.Time(), 1); Texts.WriteLn(W);
  809.     Texts.Append(Oberon.Log, W.buf)
  810.   END TimeStamp;
  811. where
  812.   VAR W: Texts.Writer;
  813. is globally defined initialized by Texts.OpenWriter(W).
  814. Remarks:
  815. 1. Normally, one (global) writer per module is sufficient.
  816. 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.
  817. Process selected text
  818.   PROCEDURE CountWords;
  819.     VAR T: Texts.Text; R: Texts.Reader;
  820.       beg, end, pos, time: LONGINT; words: INTEGER; ch: CHAR;
  821.   BEGIN words := 0;
  822.    Oberon.GetSelection(T, beg, end, time); (*get most recent selection*)
  823.     IF time >= 0 THEN (*if it exists*)
  824.       Texts.OpenReader(R, T, beg); pos := beg; (*setup reader and initialize pos*)
  825.       Texts.Read(R, ch); INC(pos); (*read next character*)
  826.       IF (pos # end) & (ch > " ") THEN
  827.          REPEAT Texts.Read(R, ch); INC(pos) UNTIL (pos = end) OR (ch <= " ");
  828.          INC(words)
  829.       END;
  830.       WHILE pos # end DO
  831.           (*(pos # end) & (ch <= " ")*)
  832.           REPEAT Texts.Read(R, ch); INC(pos) UNTIL (pos = end) OR (ch > " ");
  833.           IF pos # end THEN
  834.             REPEAT Texts.Read(R, ch); INC(pos) UNTIL (pos = end) OR (ch <= " ");
  835.             INC(words)
  836.           END
  837.        END
  838.     END;
  839.     Texts.WriteString(W, "WordCount = "); Texts.WriteInt(W, words, 1); Texts.WriteLn(W);
  840.     Texts.Append(Oberon.Log, W.buf) (*append to system log*)
  841.   END CountWords;
  842. where again
  843.   VAR W: Texts.Writer;
  844. is globally defined and initialized by Texts.OpenWriter(W).
  845. Open a viewer in system track, generate, and display text data
  846.   PROCEDURE Directory;
  847.      VAR Menu, Main: TextFrames.Frame; T: Texts.Text; V: Viewers.Viewer; X, Y: INTEGER;
  848.   BEGIN
  849.      T := TextFrames.Text(""); (*generate new (and empty) text to be displayed in a frame*)
  850.      Menu := TextFrames.NewMenu("Directory", StandardMenu); (*generate standard menu frame*)
  851.      Main := TextFrames.NewText(T, 0); (*generate standard text frame*)
  852.      Oberon.AllocateSystemViewer(Oberon.Par.vwr.X, X, Y);
  853.      V := MenuViewers.New(Menu, Main, TextFrames.menuH, X, Y); (*open standard menu viewer*)
  854.      TextFrames.Mark(Main, -1); (*setup vertical arrow mark*)
  855.      Diskette.Enumerate(Lister); (*pass over Lister-procedure to enumerator*)
  856.      Texts.Append(T, W.buf); (*append writer to T and display written text*)
  857.      TextFrames.Mark(Main, 1) (*restore position mark*)
  858.   END Directory;
  859. where
  860.   CONST StandardMenu = "System.Close System.Copy System.Grow Edit.Search Edit.Store";
  861.    VAR T: Texts.Text; W: Texts.Writer;
  862. are globally defined, W is globally initialized by Texts.OpenWriter(W), and Lister is an (upcalled) procedure displaying directory entries:
  863.   PROCEDURE* Lister (name: ARRAY OF CHAR; date, time: INTEGER; size: LONGINT);
  864.   BEGIN
  865.     Texts.WriteString(W, name);
  866.     Texts.Write(W, " "); Texts.WriteInt(W, size, 1);
  867.     Texts.Write(W, " "); Texts.WriteDate(W, time, date);
  868.     Texts.WriteLn(W)
  869.   END Lister;
  870. Remarks:
  871. 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.
  872. 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
  873.   PROCEDURE AllocateSystemViewer (DX: INTEGER; VAR X, Y: INTEGER);
  874.     VAR bot: Viewers.Viewer;
  875.   BEGIN
  876.     IF Oberon.Pointer.on THEN X := Oberon.Pointer.X; Y := Oberon.Pointer.Y
  877.     ELSE bot := Viewers.This(Oberon.SystemTrack(DX), 0); X := bot.X; Y := bot.H - Viewers.minH
  878.     END
  879.   END AllocateSystemViewer;
  880. 3. TextFrames.NewText generates a standard text frame. The following statement sequence produce a text frame with an individual handler and a customized geometry.
  881.    NEW(F); Open(F, Handle, text, pos, col, left, right, top, bot, asr, dsr, lsp);
  882. where F is of type TextFrames.Frame.
  883. Open a viewer in user track and display existing text
  884.   PROCEDURE OpenText;
  885.     VAR par: Oberon.ParList; Text: TextFrames.Frame; S: Texts.Scanner;
  886.     V: Viewers.Viewer; X, Y: INTEGER;
  887.   BEGIN
  888.     par := Oberon.Par; (*access parameters*)
  889.     Text := par.frame(TextFrames.Frame); (*calling frame*)
  890.     TextFrames.Mark(Text, -1); (*arrow mark*)
  891.     Texts.OpenScanner(S, par.text, par.pos); (*open scanner at position of parameter list*)
  892.     Texts.Scan(S); (*get symbol*)
  893.     IF S.class = Texts.Name THEN
  894.       Oberon.AllocateUserViewer(par.vwr.X, X, Y);
  895.       V := MenuViewers.New(
  896.          TextFrames.NewMenu(S.s, StandardMenu);
  897.          TextFrames.NewText(TextFrames.Text(S.s), 0);
  898.          TextFrames.menuH, X, Y);
  899.     END;
  900.     TextFrames.Mark(Text, 1) (*restore position mark*)
  901.   END OpenText;
  902. Remark:
  903. 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.
  904. Grow viewer
  905.    PROCEDURE Grow;
  906.      VAR V, newV: Viewers.Viewer; M: Oberon.CopyMsg; N: Viewers.ViewerMsg; DH: INTEGER;
  907.    BEGIN
  908.      V := Oberon.Par.vwr; (*get originator viewer*)
  909.      DH := Oberon.DisplayHeight(V.X); (*get height of this track*)
  910.      IF V.H < Oberon.DisplayHeight(V.X) THEN (*if viewer is small*)
  911.        Oberon.OpenTrack(V.X, V.W); (*open overlaying track*)
  912.        V.handle(V, M); newV := M.F(Viewers.Viewer); (*get a copy of the viewer*)
  913.        Viewers.Open(newV, V.X, DH); (*open new big viewer*)
  914.        N.id := Viewers.restore; newV.handle(newV, N) (*ask new viewer to draw itself*)
  915.      END
  916.    END Grow;
  917. Remark:
  918. 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 procedure calls. 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.
  919. Process viewer text or sequence of texts, depending on context
  920.   PROCEDURE ProcessText;
  921.     VAR par: Oberon.ParList; Main: TextFrames.Frame; S: Texts.Scanner; T: Texts.Text;
  922.   BEGIN
  923.     par := Oberon.Par; (*access parameters*)
  924.     IF par.frame = par.vwr.dsc THEN (*command in menu frame*)
  925.       IF par.vwr.dsc.next IS TextFrames.Frame THEN
  926.         Main := par.vwr.dsc.next(TextFrames.Frame); (*main text frame*)
  927.         TextFrames.Mark(Main, -1) (*set arrow mark*)
  928.         Process(Main.text); (*process displayed text*)
  929.         TextFrames.Mark(Main, 1) (*restore position mark*)
  930.       END
  931.     ELSE (*command in main text frame*)
  932.       Main := par.frame(TextFrames.Frame);
  933.       TextFrames.Mark(Main, -1) (*set arrow mark*)
  934.       Texts.OpenScanner(S, par.text, par.pos); (*open scanner at position of parameter list*)
  935.       Texts.Scan(S); (*get first symbol*)
  936.       WHILE S.class = Texts.Name DO
  937.         Texts.Open(T, S.s); (*open text from file*)
  938.         Process(T); (*process this text*)
  939.         Texts.Scan(S) (*get next symbol*)
  940.        END;
  941.        TextFrames.Mark(Main, 1) (*restore position mark*)
  942.      END
  943.   END ProcessText;
  944. Delete selected part of text in marked viewer
  945.   PROCEDURE Delete;
  946.     VAR Main: TextFrames.Frame; V: Viewers.Viewer;
  947.   BEGIN
  948.     V := Oberon.MarkedViewer(); (*get marked viewer*)
  949.     Main := V.dsc.next(TextFrames.Frame); (*main text frame of marked viewer*)
  950.     IF Main.sel > 0 THEN (*if there exists a selection*)
  951.       Texts.Delete(Main.text, Main.selbeg.pos, Main.selend.pos) (*delete text*)
  952.     END
  953.   END Delete;
  954. Copy most recently selected text part to caret's position
  955.   PROCEDURE CopyText;
  956.     VAR Main: TextFrames.Frame; buf: Texts.Buffer; V: Viewers.Viewer; time: LONGINT;
  957.   BEGIN
  958.     Oberon.GetSelection(T, beg, end, time); (*get most recent selection*)
  959.     IF time >= 0 THEN (*if it exists*)
  960.       Texts.OpenBuffer(buf);
  961.       Texts.Save(T, beg, end, buf); (*save text in buffer*)
  962.       V := Oberon.FocusViewer; (*get focus viewer*)
  963.       IF (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN (*if text viewer*)
  964.         Main := V.dsc.next(TextFrames.Frame); (*main text frame*)
  965.         IF Main.car > 0 THEN (*if caret set*)
  966.           Texts.Insert(Main.text, Main.carloc.pos, buf) (*insert text at caret's position*)
  967.         END
  968.       END
  969.     END
  970.   END CopyText;
  971. Copy font from visibly marked position to text selection
  972.   PROCEDURE CopyFont;
  973.     VAR F: TextFrames.Frame; T: Texts.Text; R: Texts.Reader; V: Viewers.Viewer;
  974.       beg, end, time: LONGINT; X, Y: INTEGER; ch: CHAR;
  975.   BEGIN
  976.     Oberon.GetSelection(T, beg, end, time); (*get most recent selection*)
  977.     IF (time >= 0) & Oberon.Pointer.on THEN (*if found and pointer visible*)
  978.       X := Oberons.Pointer.X; Y := Oberon.Pointer.Y;
  979.       V := Viewers.This(X, Y); (*marked viewer*)
  980.       IF (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN
  981.         F := V.dsc.next(TextFrames.Frame);
  982.         IF (X >= F.X) & (X < F.X + F.W) & (Y >= F.Y) & (Y < F.Y + F.H) THEN
  983.           Texts.OpenReader(R, F.text, TextFrames.Pos(F, X, Y)); (*position reader*)
  984.           Texts.Read(R, ch); (*read marked char*)
  985.           Texts.ChangeLooks(T, beg, end, {0}, R.fnt, 0, 0) (*change font alone*)
  986.         END
  987.       END
  988.     END
  989.   END CopyFont;
  990. Move caret to next character written in italics
  991.   PROCEDURE SearchItalics;
  992.     VAR Main: TextFrames.Frame; R: Texts.Reader; italic: Fonts.Font; V: Viewers.Viewer;
  993.       pos: LONGINT; ch: CHAR;
  994.   BEGIN
  995.     italic := Fonts.This("Syntax10i.Scn.Fnt");
  996.     V := Oberon.FocusViewer; (*get focus viewer*)
  997.     IF (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN (*if text viewer*)
  998.       Main := V.dsc.next(TextFrames.Frame); (*main text frame*)
  999.       IF Main.car > 0 THEN (*if caret set*)
  1000.         Texts.OpenReader(R, Main.text, Main.carloc.pos); (*open reader at caret's position*)
  1001.         Texts.Read(R, ch);
  1002.         WHILE ~R.eot & (R.fnt # italic) DO Texts.Read(R, ch) END; (*read char stream*)
  1003.         IF ~R.eot THEN (*not end of text*)
  1004.           pos := Texts.Pos(R); (*reader's position*)
  1005.           TextFrames.RemoveSelection(Main); (*remove all marks*)
  1006.           TextFrames.RemoveCaret(Main);
  1007.           Oberon.RemoveMarks(Main.X, Main.Y, Main.W, Main.H);
  1008.           TextFrames.Show(Main, Max(0, pos - 200)); (*show text at pos*)
  1009.           TextFrames.SetCaret(Main, pos) (*set caret to new position*)
  1010.         END
  1011.       END
  1012.     END
  1013.   END SearchItalics;
  1014. where Max is the maximum-function.
  1015. Guide for Programmers of new Frame Classes and Viewer Types
  1016. Frames as Active Objects
  1017. 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.
  1018.     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.
  1019.     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.
  1020.     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.
  1021. The Canonical Decomposition of an Application
  1022. 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.
  1023.     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:
  1024. Tool Package                        Edit                Draw                  Paint                 MyTool
  1025. Comm. Int. & Disp. Handler    TextFrames      GraphicFrames    PictureFrames    MyFrames
  1026. Data Manager                    Texts                Graphics             Pictures             MyData
  1027. 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.
  1028. Tutorial Example: Text Viewers
  1029. 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.
  1030.     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.
  1031.     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:
  1032.     Viewers.Track     MenuViewers.Viewer
  1033.              ^                            ^
  1034.       Viewers.Viewer    TextFrames.Frame
  1035.                   ^                ^
  1036.                   Display.Frame
  1037. 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.
  1038. In definition of Display:
  1039.     TYPE
  1040.       Frame = POINTER TO FrameDesc;
  1041.       FrameMsg = RECORD END;
  1042.       Handler = PROCEDURE (Frame, VAR FrameMsg);
  1043.       FrameDesc = RECORD
  1044.          dsc, next: Frame; (*son, brother*)
  1045.          X, Y, W, H: INTEGER;
  1046.          handle: Handler
  1047.       END;
  1048. In the definition of Viewers:
  1049.     TYPE
  1050.      Viewer = POINTER TO ViewerDesc;
  1051.       ViewerDesc = RECORD (Display.FrameDesc)
  1052.         state: INTEGER
  1053.       END;
  1054. In the definition of MenuViewers:
  1055.     TYPE
  1056.       Viewer = POINTER TO ViewerDesc;
  1057.       ViewerDesc = RECORD (Viewers.ViewerDesc)
  1058.         menuH: INTEGER
  1059.       END;
  1060. 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).
  1061. In definition of Viewers:
  1062.       ViewerMsg = RECORD (Display.FrameMsg)
  1063.         id: INTEGER;
  1064.         X, Y, W, H: INTEGER; (*new rectangle*)
  1065.         state: INTEGER (*new state*)
  1066.       END;
  1067.       (*signals change of viewer state
  1068.         id = 0: restore viewer
  1069.               1: modify size at bottom
  1070.               2: suspend viewer*)
  1071. In definition of TextFrames:
  1072.       UpdateMsg = RECORD (Display.FrameMsg)
  1073.          id: INTEGER; (*operation*)
  1074.          text: Texts.Text; (*edited text*)
  1075.          beg, end: LONGINT (*stretch*)
  1076.       END;
  1077.       (*signals change of contents
  1078.          id = 0: stretch [beg, end[ replaced
  1079.                1: stretch [beg, end[ inserted
  1080.                2: stretch [beg, end[ deleted*)
  1081. In definition of Oberon:
  1082.         InputMsg = RECORD (Display.FrameMsg)
  1083.             id: INTEGER; (*operation*)
  1084.             modes, keys: SET; (*mouse*)
  1085.             X, Y: INTEGER; (*position*)
  1086.             ch: CHAR (*character read*)
  1087.         END;
  1088.         (*signals input event
  1089.           id = 0: track mouse
  1090.                 1: consume character*)
  1091.         ControlMsg = RECORD (Display.FrameMsg)
  1092.            id: INTEGER; (*operation*)
  1093.            X, Y: INTEGER (*current location of the mous*)
  1094.         END;
  1095.          (*signals control action
  1096.              id = 0: remove focus
  1097.                    1: remove all marks
  1098.                    2: : mark*)
  1099.         SelectionMsg = RECORD (Display.FrameMsg)
  1100.               time: LONGINT;
  1101.               text: Texts.Text;
  1102.               beg, end: LONGINT
  1103.         END;
  1104.          (*signals inquiry for most recent text selection*)
  1105.          CopyOverMsg = RECORD (Display.FrameMsg)
  1106.              text: Texts.Text;
  1107.              beg, end: LONGINT
  1108.          END;
  1109.          (*receive text stretch [beg, end[*)
  1110.           CopyMsg* = RECORD (Display.FrameMsg)
  1111.               F: Display.Frame
  1112.           END;
  1113.           (*request for the creation of a copy of a text frame*)
  1114. 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
  1115.            ModifyMsg* = RECORD (Display.FrameMsg)
  1116.               id: INTEGER; (*operation*)
  1117.               dY, Y, H: INTEGER (*vector dY, new Y and H*)
  1118.            END;
  1119.            (*vertically move and extend or reduce frame at bottom*)
  1120.                id = 0: extend frame
  1121.                id = 1: reduce frame*)
  1122. 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.
  1123. 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.
  1124.     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.
  1125. 1  PROCEDURE Handle (F: Display.Frame; VAR M: Display.FrameMsg);
  1126. 2      VAR F1: Frame;
  1127. 3  BEGIN
  1128. 4    WITH F: Frame DO
  1129. 5      IF M IS Oberon.InputMsg THEN
  1130. 6        WITH M: Oberon.InputMsg DO
  1131. 7          IF M.id = Oberon.track THEN Edit(F, M.X, M.Y, M.keys)
  1132. 8          ELSIF M.id = Oberon.consume THEN
  1133. 9            IF F.car # 0 THEN Write(F, M.ch, M.fnt, M.col, M.voff) END
  1134. 12         END
  1135. 13       END
  1136. 14     ELSIF M IS Oberon.ControlMsg THEN
  1137. 15       WITH M: Oberon.ControlMsg DO
  1138. 16         IF M.id = Oberon.defocus THEN Defocus(F)
  1139. 17          ELSIF M.id = Oberon.neutralize THEN Neutralize(F)
  1140. 18          END
  1141. 19        END
  1142. 20     ELSIF M IS Oberon.SelectionMsg THEN
  1143. 21       WITH M: Oberon.SelectionMsg DO GetSelection(F, M.text, M.beg, M.end, M.time) END
  1144. 22     ELSIF M IS Oberon.CopyOverMsg THEN
  1145. 23       WITH M: Oberon.CopyOverMsg DO CopyOver(F, M.text, M.beg, M.end) END
  1146. 24     ELSIF M IS Oberon.CopyMsg THEN
  1147. 25       WITH M: Oberon.CopyMsg DO Copy(F, F1); M.F := F1 END
  1148. 26     ELSIF M IS MenuViewers.ModifyMsg THEN
  1149. 27       WITH M: MenuViewers.ModifyMsg DO Modify(F, M.id, M.dY, M.Y, M.H) END
  1150. 28     ELSIF M IS UpdateMsg THEN
  1151. 29        WITH M: UpdateMsg DO
  1152. 30          IF F.text = M.text THEN Update(F, M) END
  1153. 31        END
  1154. 32      END
  1155. 33    END
  1156. 34  END Handle;
  1157. Explanations:
  1158. 1    procedure of type Display.Handler
  1159. 2    auxiliary variable for frame copy (line 25). Needed because M.F is base type
  1160. 4    type guard; F must be a text frame
  1161. 5    type test; is message an Oberon input message?
  1162. 6    type guard
  1163. 7    if message demands mouse tracking
  1164. 8    if message demands consuming then consume a character
  1165. 9    if caret is active in this text frame
  1166. 14    type test; is message an Oberon control message?
  1167. 15    type guard
  1168. 16    if message demands removing the caret
  1169. 17    if message demands removing caret and selection
  1170. 20    type test; is message an Oberon selection inquiry?
  1171. 21    if so, register own selection if it is newer than candidate
  1172. 22    type test; is message a copy-over-message?
  1173. 23    if so, copy text stretch to caret's location in this frame
  1174. 24    type test; is message a copy-message?
  1175. 25    if so, produce a copy of this frame (initialized to empty)
  1176. 26    type test; is message a modify-message?
  1177. 27    if so, modify size or position of this frame (see below)
  1178. 28    type test; is message an update message?
  1179. 30    if so, check if changed text is represented in this frame and then update frame
  1180. We also provide the following refinement of the procedure TextFrames.Modify called in line 27 in TextFrames.Handle. It shows well how subframes of menu-viewers should react on a MenuViewers.ModifyMsg.
  1181.   PROCEDURE Modify (F: Frame; id, dY, Y, H: INTEGER);
  1182.   BEGIN
  1183.     Mark(F, 0); RemoveMarks(F); (*remove position-bar, caret, and selection*)
  1184.     IF id = MenuViewers.extend THEN
  1185.        IF dY > 0 THEN (*if frame is to be moved*)
  1186.          Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + dY, 0); F.Y := F.Y + dY (*move original block*)
  1187.        END;
  1188.        Extend(F, Y) (*extend moved frame at its bottom*)
  1189.     ELSIF id = MenuViewers.reduce THEN
  1190.       Reduce(F, Y + dY); (*reduce original frame at its bottom*)
  1191.       IF dY > 0 THEN (*if frame is to be moved*)
  1192.         Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, Y, 0); F.Y := Y (*move reduced block*)
  1193.       END
  1194.     END;
  1195.     IF F.H > 0 THEN Mark(F, 1) END (*restore position-bar*)
  1196.   END Modify;
  1197. Remember that dY is always non-negative, and notice that the reduce-part of the procedure is the exact inverse of the extend-part.
  1198.     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.
  1199.     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.
  1200.     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.
  1201.     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.
  1202.     We now present some typical examples of implementations.
  1203. Tutorial Examples 1: Frame oriented operations
  1204. Display text line within text frame
  1205.   PROCEDURE DisplayLine (F: Frame; L: Line; VAR R: Texts.Reader; X, Y: INTEGER; len: LONGINT);
  1206.     VAR pat: Display.Pattern; NX, dx, x, y, w, h: INTEGER;
  1207.   BEGIN NX := F.X + F.W;
  1208.     WHILE (nextCh # CarriageReturn) & (R.fnt # NIL) DO
  1209.       Display.GetChar(R.fnt.raster, nextCh, dx, x, y, w, h, pat);
  1210.       IF (X + x + w <= NX) & (h # 0) THEN
  1211.          Display.CopyPattern(R.col, pat, X + x, Y + y, 2)
  1212.       END;
  1213.        X := X + dx; INC(len); Texts.Read(R, nextCh)
  1214.      END;
  1215.      L.len := len + 1; L.wid := X + eolW - (F.X + F.margW);
  1216.      L.eot := R.fnt = NIL; Texts.Read(R, nextCh)
  1217.   END DisplayLine;
  1218. 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.
  1219.   Line = POINTER TO LineDesc;
  1220.   LineDesc = RECORD
  1221.     len: LONGINT; (*number of characters in this line*)
  1222.     wid: INTEGER; (*width of line box*)
  1223.     eot: BOOLEAN; (*end of text flag*)
  1224.     next: Line (*next lower neighbour*)
  1225.   END;
  1226.   Location = RECORD
  1227.     org, pos: LONGINT; (*line origin, position*)
  1228.     dx, x, y: INTEGER; (*width and position of located character*)
  1229.     lin: Line (*associated line*)
  1230.   END;
  1231.   Frame = POINTER TO FrameDesc;
  1232.   FrameDesc = RECORD (Display.FrameDesc)
  1233.     text: Texts.Text; (*displayed text*)
  1234.     org: LONGINT; (*position in text of first displayed character*)
  1235.     col: INTEGER; (*background color*)
  1236.     lsp, asr, dsr: INTEGER; (*line spacing, ascender, descender*)
  1237.     left, right, top, bot: INTEGER; (*margins*)
  1238.     markH: INTEGER; (*margin width, position of mark*)
  1239.     time: LONGINT; (*time of latest selection*)
  1240.     mark, car, sel: INTEGER; (*state of mark, caret, selection*)
  1241.     carloc: Location; (*caret location*)
  1242.     selbeg, selend: Location (*locations of begin and end of selection*)
  1243.     trailer: Line (*pointer to the associated sequence of line descriptors*)
  1244.     END;
  1245. Track caret
  1246.   PROCEDURE TrackCaret (F: Frame; X, Y: INTEGER; VAR keysum: SET);
  1247.     VAR loc: Location; keys: SET;
  1248.   BEGIN
  1249.     IF F.trailer.next # F.trailer THEN
  1250.       LocateChar(F, X - F.X, Y - F.Y, F.carloc);
  1251.       FlipCaret(F);
  1252.       keysum := {};
  1253.       LOOP
  1254.         Input.Mouse(keys, X, Y);
  1255.          IF keys = {} THEN EXIT END;
  1256.          keysum := keysum + keys;
  1257.          Oberon.DrawCursor(Oberon.Mouse, Oberon.Mouse.marker, X, Y);
  1258.          LocateChar(F, X - F.X, Y - F.Y, loc);
  1259.         IF loc.pos # F.carloc.pos THEN FlipCaret(F); F.carloc := loc; FlipCaret(F) END
  1260.       END;
  1261.       F.car := 1
  1262.     END
  1263.   END TrackCaret;
  1264. where the following auxiliary procedures are used:
  1265.    PROCEDURE LocateChar (F: Frame; x, y: INTEGER; VAR loc: Location);
  1266.     VAR R: Texts.Reader;
  1267.       pat: Display.Pattern;
  1268.       pos, lim: LONGINT;
  1269.       ox, dx, u, v, w, h: INTEGER;
  1270.   BEGIN LocateLine(F, y, loc);
  1271.     lim := loc.org + loc.lin.len - 1;
  1272.     pos := loc.org; ox := F.left;
  1273.     Texts.OpenReader(R, F.text, loc.org); Texts.Read(R, nextCh);
  1274.     LOOP
  1275.       IF pos = lim THEN dx := eolW; EXIT END;
  1276.       Display.GetChar(R.fnt.raster, nextCh, dx, u, v, w, h, pat);
  1277.       IF ox + dx > x THEN EXIT END;
  1278.       INC(pos); ox := ox + dx; Texts.Read(R, nextCh)
  1279.     END;
  1280.     loc.pos := pos; loc.dx := dx; loc.x := ox
  1281.   END LocateChar;
  1282.   PROCEDURE LocateLine (F: Frame; y: INTEGER; VAR loc: Location);
  1283.     VAR T: Texts.Text; L: Line; org: LONGINT; cury: INTEGER;
  1284.    BEGIN T := F.text;
  1285.     org := F.org; L := F.trailer.next; cury := F.H - F.top - F.asr;
  1286.     WHILE (L.next # F.trailer) & (cury > y + F.dsr) DO
  1287.       org := org + L.len; L := L.next; cury := cury - F.lsp
  1288.     END;
  1289.     loc.org := org; loc.lin := L; loc.y := cury
  1290.   END LocateLine;
  1291.   PROCEDURE FlipCaret (F: Frame);
  1292.    BEGIN
  1293.     IF F.carloc.x < F.W THEN
  1294.       IF (F.carloc.y >= 10) & (F.carloc.x + 12 < F.W) THEN
  1295.         Display.CopyPattern(white, BigCaret, F.X + F.carloc.x, F.Y + F.carloc.y - 10, 2)
  1296.       ELSIF (F.carloc.y >= 4) & (F.carloc.x + 6 < F.W) THEN
  1297.         Display.CopyPattern(white, SmallCaret, F.X + F.carloc.x, F.Y + F.carloc.y - 4, 2)
  1298.       END
  1299.     END
  1300.   END FlipCaret;
  1301. Tutorial Examples 2: Text oriented operations
  1302. Save text in buffer
  1303.   PROCEDURE Save (T: Text; beg, end: LONGINT; B: Buffer);
  1304.     VAR p, q, qb, qe: Piece; org: LONGINT;
  1305.   BEGIN
  1306.     IF end > T.len THEN end := T.len END;
  1307.     FindPiece(T, beg, org, p);
  1308.     NEW(qb); qb^ := p^;
  1309.     qb.len := qb.len - (beg - org);
  1310.     qb.off := qb.off + (beg - org);
  1311.     qe := qb;
  1312.     WHILE end > org + p.len DO
  1313.       org := org + p.len; p := p.next;
  1314.       NEW(q); q^ := p^; qe.next := q; q.prev := qe; qe := q
  1315.     END;
  1316.     qe.next := NIL; qe.len := qe.len - (org + p.len - end);
  1317.     B.last.next := qb; qb.prev := B.last; B.last := qe;
  1318.     B.len := B.len + (end - beg)
  1319.   END Save;
  1320. where FindPiece is the following auxiliary procedure:
  1321.   PROCEDURE FindPiece (T: Text; pos: LONGINT; VAR org: LONGINT; VAR p: Piece);
  1322.      VAR n: INTEGER;
  1323.   BEGIN
  1324.     IF pos < T.org THEN T.org := -1; T.pce := T.trailer END;
  1325.     org := T.org; p := T.pce; (*from cache*)
  1326.     n := 0;
  1327.     WHILE pos >= org + p.len DO org := org + p.len; p := p.next; INC(n) END;
  1328.     IF n > 50 THEN T.org := org; T.pce := p END (*to cache*)
  1329.   END FindPiece;
  1330. 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.
  1331.   Piece = POINTER TO PieceDesc;
  1332.   PieceDesc = RECORD
  1333.       f: Files.File; (*carrier file*)
  1334.       off: LONGINT; (*offset in file*)
  1335.       len: LONGINT; (*piece length*)
  1336.       fnt: Fonts.Font; (*font*)
  1337.       col: SHORTINT; (*color*)
  1338.       voff: SHORTINT; (*vertical offset*)
  1339.       prev, next: Piece (*links to neighbours*)
  1340.    END;
  1341.    Text = POINTER TO TextDesc;
  1342.    Notifier = PROCEDURE (Text, INTEGER, LONGINT, LONGINT);
  1343.    TextDesc = RECORD
  1344.       len: LONGINT; (*text length*)
  1345.       notify: Notifier; (*called after text changes*)
  1346.       trailer: Piece; (*sentinel*)
  1347.       org: LONGINT; (*cached origin*)
  1348.       pce: Piece (*cached piece*)
  1349.     END;
  1350.   Buffer = POINTER TO BufDesc;
  1351.   BufDesc = RECORD
  1352.      len: LONGINT; (*buffer length*)
  1353.      header, last: Piece (*buffered piece list*)
  1354.   END;
  1355. Insert contents of buffer in text
  1356.   PROCEDURE Insert (T: Text; pos: LONGINT; B: Buffer);
  1357.      VAR pl, pr, p, qb, qe: Piece; org: LONGINT;
  1358.   BEGIN
  1359.     FindPiece(T, pos, org, p); SplitPiece(p, pos - org, pr);
  1360.     IF T.org >= org THEN (*adjust cache*)
  1361.        T.org := org - p.prev.len; T.pce := p.prev
  1362.     END;
  1363.     pl := pr.prev; qb := B.header.next;
  1364.     IF (qb # NIL) & (qb.f = pl.f) & (qb.off = pl.off + pl.len)
  1365.        & (qb.fnt = pl.fnt) THEN pl.len := pl.len + qb.len; qb := qb.next
  1366.      END;
  1367.      IF qb # NIL THEN qe := B.last;
  1368.         qb.prev := pl; pl.next := qb; qe.next := pr; pr.prev := qe
  1369.      END;
  1370.      T.len := T.len + B.len;
  1371.      T.notify(T, insert, pos, pos + B.len); (*call postprocessor*)
  1372.      B.last := B.header; B.last.next := NIL; B.len := 0
  1373.   END Insert;
  1374. where SplitPiece is
  1375.  PROCEDURE SplitPiece (p: Piece; off: LONGINT; VAR pr: Piece);
  1376.     VAR q: Piece;
  1377.   BEGIN
  1378.      IF off > 0 THEN NEW(q);
  1379.        q.col := p.col; q.fnt := p.fnt;
  1380.        q.len := p.len - off;
  1381.        q.f := p.f; q.off := p.off + off;
  1382.        p.len := off;
  1383.        q.next := p.next; p.next := q;
  1384.        q.prev := p; q.next.prev := q;
  1385.        pr := q
  1386.     ELSE pr := p
  1387.     END
  1388.  END SplitPiece;
  1389. Literature
  1390. Ceres Workstation
  1391. H. Eberle. Development and Analysis of a Workstation Computer.
  1392.     Diss. ETH No. 8431, 1987.
  1393. B. Heeb. Design of the Processor-Board for the Ceres-2 Workstation,
  1394.     Bericht  93, Inst. f
  1395. r Informatik, ETH Z
  1396. rich, November 1988.
  1397. Oberon Language
  1398. N. Wirth and M.Reiser:  Programming in Oberon.
  1399.     To be published by Addison-Wesley in early 1992.
  1400. N. Wirth. Type Extensions.
  1401.     ACM Trans. on Prog. Languages and Systems, 10, 2 (April 1988), 204-214.
  1402. N. Wirth. From Modula to Oberon.
  1403.     Software - Practice and Experience, 18, 7, (July 1988), 661-670.
  1404. N. Wirth. The Programming Language Oberon.
  1405.     Software - Practice and Experience, 18, 7, (July 1988),     671- 690.
  1406. J. Gutknecht. Variations on the Role of Module Interfaces.
  1407.     Structured Programming, 10, 1, (Jan. 1989), 40-46.
  1408. Oberon System
  1409. M. Reiser:  The Oberon System, Reference and User's Guide.
  1410.     Addison Wesley, February 1991.
  1411. N. Wirth and J. Gutknecht:  Project Oberon.
  1412.     To be published by Addison-Wesley in early 1992.
  1413. N. Wirth:  Designing a System from Scratch.
  1414.     Structured Programming, 10, 1 (Jan. 1989), 10-18.
  1415. N. Wirth and J. Gutknecht:  The Oberon System.
  1416.     Software - Practice and Experience, 19, 9 (Sept. 1989), 857-893.
  1417.