Kompakter Texteditor als Modul

Kennen Sie das? Sie möchten in Ihrem Programm mehrzeilige Texteingaben ermöglichen, wobei z.b. die Zeilenlänge aufgrund der Bildschirmaufteilung begrenzt ist oder sich sogar von Fall zu Fall ändert.

Kein Problem... mit FORM INPUT müßte es gehen: Zeilenlänge wird angegeben, BACKSPACE und DELETE geht, Cursor hin und her geht auch. Aber was ist mit Cursor rauf und runter oder den Funktions- oder Sondertasten? Dann ist da noch die Sache mit dem Bildschirm-Scrolling und der Speicherverwaltung; außerdem soll das Hauptprogramm nicht unnötig aufgebläht werden, und die Textzeilen müssen leicht zugänglich sein. Doch ein Problem? Nicht mit ED_IT!

Das nachstehende Modul erfüllt die gestellten Anforderungen auf einfache und durchschaubare Weise: Tastendruck abwarten - Taste aus werten - falls ASCII-Zeichen, dieses an Zeile anhängen - falls Steuertaste, Aktionen ausführen - fertig! Cursor- und Bildschirmsteuerung werden mit VT52-Sequenzen erledigt, so daß das ganze „Grafikverwaltungs- und Cursor-wo-bist-Du-Geraffel“ entfällt. Diese Sequenzen werden normalerweise für die Ansteuerung von Bildschirmterminals des Typs 52 verwendet. Solche Terminals werden in Großrechneranlagen verwendet, wo mehrere Arbeitsplätze mit einem Zentralcomputer verbunden sind, und bestehen eigentlich nur aus Bildschirm, Tastatur und Zwischenspeicher. In den empfangenen Texten, die auf den Bildschirm sollen, sind bestimmte Steuerzeichen versteckt, mit denen z.B. Cursor-Position und Schriftfarbe verändert werden oder Teile des Bildspeichers gelöscht werden können. Sie bestehen aus einer Folge von zwei oder mehr Zeichen, wobei das erste immer den Zeichencode 27 (= ASCII-Code für Escape) hat.

Eben diese Escape-Sequenzen werden auch vom Betriebssystem des Atari ST verstanden (VT52-Emulator). Soll zum Beispiel der Cursor in die linke obere Ecke gesetzt werden, sendet man an den Bildschirm ESCH:

PRINT CHR$(27);"H";

Oder soll an der momentanen Cursor-Position eine Zeile eingefügt werden, sendet man einfach ESC L:

PRINT CHR$(27);"L";

Der untere Teil des Bildschirmes wird dann automatisch verschoben. Eine Übersicht der möglichen VT52-Funktionen gibt die Tabelle.

Das Konzept

Auch wenn der Emulator die meiste Bildschirmarbeit erledigt, geht es doch nicht ganz ohne Verwaltungskram. ED_IT verwendet hierfür einige Variablen, in denen die aktuelle Zeilennummer (ZNR), die Zeilennummer der obersten Bildschirmzeile (TOPLINE) und die Position des Cursors innerhalb einer Zeile (ZEIGER) vermerkt sind (siehe auch Bild 1). Die Zeileninhalte liegen in dem Stringarray ZEILEN$(). Bei jeder Tastenaktion werden nun diese Variablen aktualisiert. Beispiel: mit jeder Eingabe eines Textzeichens wird ZEIGER um eins erhöht, das Zeichen an ZEILEN$() angehängt und der Cursor weiterbewegt. Wird eine Cursor-Taste (z.B. hoch) bedient, wird ZNR verringert; befindet sich der Cursor am oberen Bildschirmrand, wird TOPLINE zurückgezählt, der Bildschirm heruntergescrollt und die oberste Zeile neu eingetragen. Die Unterscheidung, ob eine Zeichen- oder Steuertaste gedrückt wurde, erfolgt über sogenannte Scan-Codes. Bei Tastendruck wird von der Tastatur ein 32-Bit-Wert mit der Tastenkombination geliefert, der wie folgt aufgebaut ist:

Bits 0-7 ASCII-Code der Taste
Bits 16-23 SCAN-Code der Taste
Bits 24-31 Umschalttastenstatus

Durch Ausmaskieren mit der AND-Funktion werden die benötigten Bits isoliert (siehe auch DEFFN in der Prozedur EDI_INIT). So kann man sich alle benötigten Informationen über das, was auf der Tastatur los ist, herausholen.

Die Initialisierung

In der Prozedur EDI-INIT werden die Konstanten für Bildschirmhöhe, Bildschirmbreite usw. eingelesen sowie der „Textspeicher“ ZEILEN$() dimensioniert. Auf diese Weise ist eine Anpassung an individuelle Bedürfnisse kein Problem. Die Funktionsdefinitionen ASCII(x), FTASTE(x) und SCAN(x) filtern die zur Steuerung benötigten Bits aus dem Wert, der vom Tastaturprozessor gesendet wird. Die neue Bildschirmhöhe wird dem System über eine LINE-A (L~a)-Variable mitgeteilt. In den Line-A-Variablen stehen Informationen, die GEM und TOS für Bildschirmaktionen usw. dringend benötigen. Also Vorsicht beim Experimentieren! Versehentliche Änderungen in diesem Bereich können einen Absturz, mindestens aber merkwürdiges und unberechenbares Verhalten des Rechners zur Folge haben (Sie kennen sicher das Naturgesetz, nach dem sich der Rechner aufhängt, bevor man die Arbeit von Stunden abgespeichert hat). Um den Ursprungszustand bei Verlassen des Editors wieder herstellen zu können, wird die Variable OLD_ZEILEN als Zwischenspeicher genutzt. Zum Schluß der Vorbereitungen wird der Cursor eingeschaltet und in die linke obere Ecke gesetzt. Die Sequenz ESC E löscht nur den eingestellten Bildschirmbereich. Auf diese Weise kann man unterhalb des scrollfähigen Teiles Help-Texte, Bedienungshinweise oder Programminformationen anzeigen, ohne daß diese bei Anwendung des Editors verschoben werden. Grafikfunktionen und Anzeigen mit TEXT x,y,X$ sind in diesem Bereich unbeschränkt benutzbar, so daß laufende Änderungen kein Problem sind.

Der Editor

Zunächst langweilt sich der Rechner in einer Schleife. Die Hilfsvariable X hat solange den Wert Null, bis die Tastatur den oben angesprochenen 32-Bit-Wert liefert. Anschließend wird der zugehörige ASCII-Code herausgezogen und in der Variablen TASTE gespeichert. Sollte TASTE den Wert Null haben, war es eine Sondertaste (Up, Down, Help, usw.); also wird nun der SCAN-Code ermittelt und anhand der SELECT-CASE-Anweisung verzweigt. Wurde beispielsweise die Cursor-Down-Taste (SCAN 80) gedrückt, wird die Zeilennummer ZNR um eins erhöht, ZEIGER auf den Zeilenanfang der nächsten Zeile gesetzt und geprüft, ob gescrollt werden muß. Dies ist dann der Fall, wenn sich der Cursor in der untersten Zeile des eingestellten Bildschirmbereiches befindet; also mit PRINT einen Upscroll erzwingen und die TOPLINE um eins erhöhen. Befand sich aber der Cursor oberhalb der Unterkante, wird er mit ESC B um eine Zeile nach unten bewegt. RETURN und Cursor-Up funktionieren entsprechend. ED_IT merkt sich immer die höchste Zeilennummer in der Variablen LASTLINE, damit ein Durchfahren des Textes nur bis zur letzten Zeile möglich ist. Wollen Sie den Editor um weitere Funktionen erweitern, brauchen Sie nur den SCAN-Code der Taste zu ermitteln und eine weitere „CASE-xy-Schachtel" hinzuzufügen, in der dann Ihre Funktionen liegen.

Der Editor kann mittels UNDO-Taste wieder verlassen werden. Wie schon oben erwähnt, ist es wichtig, die Line-A-Variable Int{L~a-42} auf den alten Wert zurückzusetzen, um böse Überraschungen zu vermeiden.

Literatur:

[1] Handbuch GFA-BASIC 3.0, GFA Systemtechnik GmbH
[2] Brückmann/Englisch/Gerits: ST-Intern, DATA BECKER GmbH

Übersicht der VT52-Escape-Sequenzen

ESC „A“ Cursor hoch ohne Scrolling
ESC „B“ Cursor runter ohne Scrolling
ESC „C“ Cursor rechts
ESC „D“ Cursor links
ESC „E“ Clear Home (löscht Bildschirm)
ESC „H“ Cursor Home (linke obere Ecke)
ESC „I“ Cursor hoch mit Scrolling
ESC „J“ löscht den Rest des Bildschirmes ab Cursor-Position.
ESC „K“ löscht den Rest der Zeile, in der sich der Cursor befindet.
ESC „L“ Zeile an Cursor-Position einfügen. Unterer Bildschirmteil wird um eine Zeile nach unten verschoben.
ESC „M“ entfernt die Zeile, in der der Cursor steht, und zieht den Rest des Bildschirmes hoch.
ESC „Y“ Positionieren des Cursors. Die folgenden zwei Zeichen werden als Zeile und Spalte interpretiert, wobei jeweils 32 addiert werden muß. Soll der Cursor beispielsweise auf Zeile 8, Spalte 12 gesetzt werden, schreibt man:

PRINT CHR$(27);“Y“;CHR$(8+32);CHR$(12+32);

ESC „b“ Einstellung der Schriftfarbe. Je nach Modus 2, 4 oder 16 Farben (zB. schwarz: ESC „b1“)
ESC „c“ Einstellung der Hintergrundfarbe
ESC „d“ Löschen des Bildschirmes bis Cursor-Position
ESC „e“ Einschalten des Cursors
ESC „f“ Abschalten des Cursors
ESC „j“ speichert die augenblickliche Cursor-Position
ESC „k“ setzt Cursor wieder auf gespeicherte Position.
ESC „l“ löscht nur die aktuelle Zeile.
ESC „o“ löscht von Zeilenanfang bis Cursor-Position
ESC „p“ inverse Schrift einschalten
ESC „q“ inverse Schrift abschalten
ESC „v“ Zeilenumbruch an. Bei Erreichen des Zeilenendes wird Ausgabe in der nächsten Zeile fortgesetzt.
ESC „w“ Zeilenumbruch aus

Bild 1: Zusammenhang zwischen Variablen und Textspeicher

'
' ED_IT.GFA     von Rolf Stelljes 
' Mini-Texteditor 
' GFA 3.07
' (c) MAXON Computer 1991
'
edi_init
editor
'
PROCEDURE edi_init      ! Vorbereitung der Editorfunktionen
    '
    ' Alle Numerischen Variablen Integer-Format % 
    ' Die Bildschirmsteuerungen erfolgen über VT52-Sequenzen
    '
    DIM zeilen$(500)    ! Array für Textzeilen (Größe abhängig ' 
    '                   ! von Verwendungszweck)
    DEFFN ascii(x%)=x% AND 255      ! Ermittelt ASCII-Code einer Taste 
    DEFFN ftaste(x%)=@scan(x%)-58   ! Ermittelt F-Tasten-Nummer
    DEFFN scan(x%)=((x% AND &HFFOOOO)/65536) ! Ermittelt Scan-Code 
    scr_ho%=10          ! Bildschirmhöhe
    scr_br%=30          ! Bildschirmbreite
    znr%=1              ! Erste Z-Nr.
    zeiger%=1           ! Zeilenanfang
    tabs%=8             ! Tabulatorschritte
    topline%=1          ! Oberste Zeile des Bildschirms 
    raus!=FALSE         ! Merker zurücksetzen
    WHILE INKEY$<>""    ! Tastaturpuffer leeren
    WEND
    '
    old_zeilen%=INT{L~A-42} ! Maximale Anzahl Zeilen merken 
    INT{L~A-42}=scr_ho%-1   ! Neue max Zeilenanzahl setzen
    '
    PRINT CHR$(27);"e"; ! Cursor ein
    PRINT CHR$(27);"H"; ! Cursor in linke obere Ecke
RETURN
PROCEDURE editor                ! Texteditor mit Scrolling, Ins, Del. 
    REPEAT                      ! Hauptschleife
        REPEAT                  ! Warten auf Tastendruck
            KEYTEST x%          ! und Tastencode merken
        UNTIL x%>0
        taste%=@ascii(x%)       ! ASCII-Code ausfiltern
        IF taste%=0             ! Falls Steuertaste bedient
            SELECT @scan(x%)    ! Scan-Code ermitteln 
            CASE 82             ! ** Insert **
                PRINT CHR$(27);"L"; ! Leerzeile an CrsPosition einfügen 
                INSERT zeilen$(znr%)="" ! Platz in Array einfügen 
                INC lastline%   ! Anzahl der Zeilen erhöhen 
                zeiger%=1       ! Crs an linken Rand 
            CASE 80             ! ** Down **
                IF znr%<lastline% ! Runter nur bis letzte Zeile 
                    INC znr%    ! Nächste Zeilennummer (max Lastline) 
                    zeiger%=1   ! Zeilenanfang
                    IF CRSLIN=>scr_ho%  ! Falls Unterkante Bildschirm erreicht 
                        INC topline%    ! Topline verschieben
                        PRINT           ! Bildschirm hochscrollen
                    ELSE                ! ansonsten nur
                        PRINT CHR$(27);"B"; ! Crs down
                    ENDIF
                    PRINT AT(1,CRSLIN);zeilen$(znr%);AT(1,CRSLIN); ! Zeileninhalt zeigen
                ENDIF
            CASE 72                 ! ** Up **
                znr%=MAX(1,znr%-1)  ! Vorige Zeilennummer (minimal 1) 
                zeiger%=1           ! Zeilenanfang
                IF CRSLIN=1         ! Falls oberer Bildschirmrand 
                    IF topline%>1   ! Hochscrollen nur bis Oberkante Text 
                        PRINT CHR$(27);"I"; ! Crs up mit Scrolling down
                    ENDIF
                    topline%=MAX(1,topline%-1)! Topline verschieben 
                ELSE                ! Falls nicht Bildschirmoberkante 
                    PRINT CHR$(27);"I";     ! Crs hoch
                ENDIF
                PRINT AT(1,CRSLIN);zeilen$(znr%);AT(1,CRSLIN); ! Zeileninhalt anzeigen
            CASE 77                 ! ** Right **
                IF zeiger%<LEN(zeilen$(znr%))+1     ! Zeiger maximal bis Zeilenende 
                    PRINT CHR$(27);"C";             ! Crs rechts
                    zeiger%=MIN(zeiger%+l,scr_br%)  ! Zeiger nach rechts verschieben 
                ENDIF                               !
            CASE 75                         ! ** Left **
                PRINT CHR$(27);"D";         ! Crs links
                zeiger%=MAX(zeiger%-1,1)    ! Zeiger nach links verschieben 
            CASE 97                         ! ** Undo **
                INT{L~A-42}=old_zeilen%     ! Alten Zustand wieder herstellen 
                PRINT CHR$(27);"f";         ! Crs ausschalten
                raus!=TRUE                  ! Fertig-Flag setzen
            ENDSELECT                       ! Ende Steuertastenverteiler
        ELSE
            x$=zeilen$(znr%)                ! Aktuelle Zeile in Hilfsstring legen 
            y$=x$
            SELECT taste%                   ! Falls ASCII-Zeichen
            CASE 32 TO 126,129,132,142,148,153,154,158 ! ** Zeichentaste
                IF zeiger%<scr_br%
                    x%=LEN(x$)-zeiger%+1    ! Restlänge der Zeile ab Zeiger 
                    x$=LEFT$(x$,zeiger%-1)+CHR$(taste%) ! Neues Zeichen vor Zeiger einfügen 
                    IF x%>0                 ! Falls Rest vorhanden 
                        x$=x$+RIGHT$(y$,x%) ! Restzeile anhängen
                    ENDIF
                    zeiger%=MIN(zeiger%+1,scr_br%) ! Zeiger weiter bewegen 
                    PRINT AT(1,CRSLIN);x$;  ! Zeile anzeigen 
                    PRINT AT(zeiger%,CRSLIN);! Crs positionieren 
                    zeilen$(znr%)=x$        ! Neue Zeile merken
                ENDIF
            CASE 9                          ! ** Tab **
                x%=((zeiger%+tabs%) DIV tabs%)*tabs%    ! Nächste Tab-Position berechnen 
                zeiger%=MAX(1,MIN(LEN(zeilen$(znr%))+1,x%)) ! Zeiger positionieren
                PRINT AT(zeiger%,CRSLIN);   ! Crs posit.
            CASE 13                         ! ** Return **
                INC znr%                    ! Neue Zeilennummer 
                IF CRSLIN=>scr_ho%          ! Falls Unterkante Bildschirm erreicht 
                    INC topline%            ! Topline verschieben 
                    PRINT                   ! Bildschirm hochscrollen 
                ELSE                        ! ansonsten nur...
                    PRINT CHR$(27);"B";     ! Crs runter
                ENDIF !
                PRINT AT(1,CRSLIN);zeilen$(znr%);AT(1,CRSLIN); ! Zeileninhalt anzeigen
                zeiger%=1                   ! Zeiger auf Zeilenanfang setzen
            CASE 25                         ! ** Ctl Y ** (Zeile löschen)
                PRINT CHR$(27);"M";         ! Zeile auf Bildschirm löschen mit upscroll 
                DEC lastline%               ! Anpassung Zeilenanzahl 
                DELETE zeilen$(znr%)        ! Zeile aus Array entfernen 
                zeiger%=1                   ! Zeiger auf Zeilenanfang
            CASE 27                         ! ** Escape ** (Zeileninhalt löschen) 
                PRINT CHR$(27);"l";         ! Zeile auf Bildschirm löschen 
                zeilen$(znr%)=""            ! Inhalt löschen
                zeiger%=1                   ! Zeiger auf Zeilenanfang
            CASE 8,127                      ! ** Delete/Backspace ** (Zeichen löschen) 
                x$=zeilen$(znr%)            ! Aktuelle Zeile an Hilfsstring übergeben 
                x%=LEN(x$)                  ! Länge merken
                IF taste%=8                 ! Falls Backspace-Taste
                    zeiger%=MAX(zeiger%-1,1) ! voriges Zeichen anvisieren
                ENDIF
                IF zeiger%<=x%              ! Falls Zeichen innerhalb Zeile 
                    zeilen$(znr%)=LEFT$(x$,zeiger%-1)+RIGHT$(x$,x%-zeiger%) ! dieses isolieren
                    PRINT AT(1,CRSLIN);zeilen$(znr%);" ";   ! Zeile neu anzeigen 
                    PRINT AT(zeiger%,CRSLIN);! Crs positionieren
                ENDIF
            ENDSELECT                       ! Ende ASCII-Tastenverteiler
        ENDIF
        lastline%=MAX(lastline%,znr%)       ! Höchste Zeilennummer merken
    UNTIL raus!
RETURN

Rolf Stelljes
Links

Copyright-Bestimmungen: siehe Über diese Seite