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.
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.
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.
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
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
'
' 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