DEFINITION MODULE tv; (* A small TVision/Turbo-Vision style character-cell UI toolkit in ISO GNU Modula-2 (gm2 -fiso), terminal backend. It provides a tiny retained view tree (groups/windows containing widgets) with clipping and z-order, a diffed ANSI cell screen, keyboard and mouse decoding (xterm SGR mouse reports), and widgets: static text, frame, input line, button, check box, radio buttons, list box and scroll bar. Compile (from this directory): gm2 -fiso -c tvtty.def gm2 -fiso -I. -c tv.mod gm2 -fiso -I. tv.o demo.mod -o demo The only non-Modula-2 part is tvtty.def (libc termios/ioctl/read/write). *) FROM SYSTEM IMPORT ADDRESS; CONST (* 16 terminal colours *) Black = 0; Blue = 1; Green = 2; Cyan = 3; Red = 4; Magenta = 5; Brown = 6; LightGray = 7; DarkGray = 8; LightBlue = 9; LightGreen = 10; LightCyan = 11; LightRed = 12; LightMagenta = 13; Yellow = 14; White = 15; SingleFrame = 1; DoubleFrame = 2; (* key codes *) kNone = 0; kChar = 1; kUp = 2; kDown = 3; kLeft = 4; kRight = 5; kHome = 6; kEnd = 7; kPgUp = 8; kPgDn = 9; kEnter = 10; kEsc = 11; kTab = 12; kBack = 13; kDel = 14; kIns = 15; kF1 = 20; kF2 = 21; kF3 = 22; kF4 = 23; kF5 = 24; kF6 = 25; kF7 = 26; kF8 = 27; kF9 = 28; kF10 = 29; kCtrlC = 30; kF11 = 31; kF12 = 32; kSpace = 33; (* built-in commands (button tags) *) cmNone = 0; cmClose = 1; cmOK = 2; cmCancel = 3; cmYes = 4; cmNo = 5; (* message box kinds *) mbOK = 0; mbOKCancel = 1; mbYesNo = 2; (* view flags *) vfDisabled = 1; vfSelected = 2; vfFramed = 4; vfExpand = 8; (* grows when its window is resized *) MAXLINE = 128; MAXVIEWS = 128; EDMAXLINES = 512; EDMAXCOL = 160; (* editor languages (for highlighting / keyword upcasing) *) langNone = 0; langModula2 = 1; langPascal = 2; langOberon = 3; TYPE Attr = CARDINAL; Point = RECORD x, y : INTEGER; END; Rect = RECORD x, y, w, h : INTEGER; END; EventKind = (evNone, evKey, evMouse); Event = RECORD kind : EventKind; key : INTEGER; ch : CARDINAL; ctrl : BOOLEAN; mx, my : INTEGER; mbtn : INTEGER; (* 1 left, 2 middle, 3 right *) mpressed, mreleased : BOOLEAN; mwheel : INTEGER; (* -1 up, +1 down *) END; (* ---- screen ------------------------------------------------------------ *) PROCEDURE Init() : BOOLEAN; (* Initialise for a non-terminal driver (e.g. the tigr backend): sets the virtual screen size and resets state without touching the terminal. *) PROCEDURE InitVirtual (c, r : INTEGER) : BOOLEAN; PROCEDURE Done; PROCEDURE Cols() : INTEGER; PROCEDURE Rows() : INTEGER; PROCEDURE Resized() : BOOLEAN; PROCEDURE A (fg, bg : INTEGER; blink : BOOLEAN) : Attr; PROCEDURE Clear (attr : Attr); PROCEDURE Fill (x, y, w, h : INTEGER; cp : CARDINAL; attr : Attr); PROCEDURE DrawCh (x, y : INTEGER; cp : CARDINAL; attr : Attr); PROCEDURE CellAt (x, y : INTEGER; VAR cp, attr : CARDINAL); PROCEDURE DrawText (x, y : INTEGER; s : ARRAY OF CHAR; attr : Attr); PROCEDURE DrawBox (r : Rect; style : INTEGER; attr : Attr); PROCEDURE DrawShadow (r : Rect; attr : Attr); PROCEDURE SetCursor (x, y : INTEGER); PROCEDURE HideCursor; PROCEDURE Present; PROCEDURE ReadEvent (VAR e : Event) : BOOLEAN; (* ---- text helpers ------------------------------------------------------ *) PROCEDURE TextLen (s : ARRAY OF CHAR) : INTEGER; PROCEDURE CopyText (VAR dest : ARRAY OF CHAR; s : ARRAY OF CHAR); PROCEDURE AppendText (VAR dest : ARRAY OF CHAR; s : ARRAY OF CHAR); PROCEDURE PadText (VAR dest : ARRAY OF CHAR; s : ARRAY OF CHAR; width : INTEGER); PROCEDURE IntToText (VAR dest : ARRAY OF CHAR; n : INTEGER; width : INTEGER); (* ---- view tree --------------------------------------------------------- *) TYPE VKind = (VGroup, VWindow, VText, VFrame, VInput, VButton, VCheck, VRadio, VList, VScroll, VEditor); ListData = RECORD items : ARRAY [0..255] OF ARRAY [0..63] OF CHAR; count : INTEGER; END; ListDataPtr = POINTER TO ListData; (* Multi-line editor buffer. Declare one per editor (it is a few tens of KB) at module scope and attach it to a VEditor view. *) EditorData = RECORD line : ARRAY [0..EDMAXLINES - 1] OF ARRAY [0..EDMAXCOL - 1] OF CHAR; len : ARRAY [0..EDMAXLINES - 1] OF INTEGER; count : INTEGER; cx, cy : INTEGER; (* cursor: column, line *) top, left : INTEGER; (* scroll *) ins : BOOLEAN; (* insert mode *) dirty : BOOLEAN; lang : INTEGER; (* one of the lang* constants *) indent : INTEGER; (* auto-indent step (0 = off) *) END; EditorDataPtr = POINTER TO EditorData; ViewPtr = POINTER TO View; View = RECORD kind : VKind; rect : Rect; parent, next, prev, first, last, focus : ViewPtr; clipKids : BOOLEAN; text : ARRAY [0..MAXLINE - 1] OF CHAR; len, pos : INTEGER; value, minv, maxv, tag : INTEGER; flags : CARDINAL; list : ListDataPtr; ed : EditorDataPtr; END; PROCEDURE NewView (kind : VKind; x, y, w, h : INTEGER) : ViewPtr; PROCEDURE FreeView (v : ViewPtr); PROCEDURE AddView (parent, child : ViewPtr); PROCEDURE DelView (v : ViewPtr); PROCEDURE SetText (v : ViewPtr; s : ARRAY OF CHAR); PROCEDURE SetTag (v : ViewPtr; t : INTEGER); PROCEDURE Desk () : ViewPtr; PROCEDURE DrawTree; (* draw desktop + all views *) PROCEDURE Finish; (* DrawTree + Present *) PROCEDURE HandleEvent (e : Event) : INTEGER; (* returns a command *) PROCEDURE Sender () : ViewPtr; (* view that produced it *) PROCEDURE BringToFront (v : ViewPtr); PROCEDURE FocusView (v : ViewPtr); PROCEDURE FocusNext (backwards : BOOLEAN); PROCEDURE MoveViewBy (v : ViewPtr; dx, dy : INTEGER); (* Resize a window; direct children flagged vfExpand grow by the same delta. *) PROCEDURE WindowResize (w : ViewPtr; ww, hh : INTEGER); (* Menu bar + drop-down menus. Typical use: MenuBarInit; MenuAdd("File"); MenuItemAdd("New", cNew); MenuItemAdd("Quit", cQuit); MenuAdd("Help"); MenuItemAdd("About", cAbout); Selecting an item makes HandleEvent return its tag. *) PROCEDURE MenuBarInit; PROCEDURE MenuAdd (title : ARRAY OF CHAR); PROCEDURE MenuItemAdd (text : ARRAY OF CHAR; tag : INTEGER); PROCEDURE MenuSep; PROCEDURE MenuBar (show : BOOLEAN); (* Generic modal dialog builder. Dialog() starts a centered window; the Dlg* procedures add controls top-to-bottom (inputs/checks/radios get a field index); DlgRun() shows it modally and returns the command of the button that was pressed (its tag), cmCancel on Esc, or cmClose. *) PROCEDURE Dialog (title : ARRAY OF CHAR; w, h : INTEGER); PROCEDURE DlgLabel (text : ARRAY OF CHAR); PROCEDURE DlgInput (label : ARRAY OF CHAR; initial : ARRAY OF CHAR) : INTEGER; PROCEDURE DlgCheck (label : ARRAY OF CHAR; checked : BOOLEAN) : INTEGER; PROCEDURE DlgRadio (label : ARRAY OF CHAR; selected : BOOLEAN) : INTEGER; PROCEDURE DlgButton (text : ARRAY OF CHAR; tag : INTEGER); PROCEDURE DlgRun () : INTEGER; PROCEDURE DlgTextAt (idx : INTEGER; VAR s : ARRAY OF CHAR); PROCEDURE DlgValue (idx : INTEGER) : INTEGER; (* 1 if check/radio selected *) (* Modal message box; returns cmOK/cmCancel/cmYes/cmNo. *) PROCEDURE MessageBox (title : ARRAY OF CHAR; text : ARRAY OF CHAR; kind : INTEGER) : INTEGER; (* Modal helper: a message box with an OK button. *) PROCEDURE Message (title : ARRAY OF CHAR; text : ARRAY OF CHAR); (* ---- editor ------------------------------------------------------------ *) PROCEDURE EditorAttach (v : ViewPtr; data : EditorDataPtr); PROCEDURE EditorInit (data : EditorDataPtr); PROCEDURE EditorAddLine (data : EditorDataPtr; s : ARRAY OF CHAR); PROCEDURE EditorGetLine (data : EditorDataPtr; i : INTEGER; VAR s : ARRAY OF CHAR); PROCEDURE EditorLoad (data : EditorDataPtr; path : ARRAY OF CHAR) : BOOLEAN; PROCEDURE EditorSave (data : EditorDataPtr; path : ARRAY OF CHAR) : BOOLEAN; PROCEDURE EditorDirty (data : EditorDataPtr) : BOOLEAN; PROCEDURE EditorSetLanguage (data : EditorDataPtr; lang : INTEGER); PROCEDURE EditorSetIndent (data : EditorDataPtr; n : INTEGER); END tv.