← ST-Computer 03 / 1987

Alle Macht den Tasten - Formatierte Eingaberoutine fĂŒr PASCAL

Listings

Mancher PASCAL-Programmierer kennt das Problem der Standard-Eingabefunktionen READ und READLN. Will man eine Zahl einiesen und tippe versehentlich eine Buchstabentaste, ist das Programm unzufrieden. Die folgende Routine lĂ€ĂŸt dieses Manko vergessen, bietet ganz nebenbei noch eine Formatvorgabe und begrenzt auf Wunsch die erlaubten Tasten.

Wer die Dialogboxen von GEM kennt, wird sie sicher zu schĂ€tzen wissen. Doch beim Erstellen eigener Programme ist die Handhabung dieser Boxen nicht gerade einfach, vor allem fĂŒr diejenigen, die sich noch nicht eingehend mit den Resourcen und ObjektbĂ€umen beschĂ€ftigt haben.

Doch nun zu unserer Routine. Zuerst die Syntax:

INPUT (x,y,maxlaenge,default,erlaubt,formatstring,inputstr)

wobei

x = x-Position der Zeile (integer)
y = y-Position der Zeile (integer)
maxlaenge = LĂ€nge des einzugebenden Strings (integer)
default = vorbelegt er Text im Eingabestring (string)
erlaubt = gĂŒltige Eingabetasten ([’A’..’Z’], [’a’..’z’], [’0’..’9’] oder auch [’E’-.’H’]
formatstring = Eingabemaske, (string) ’__’ und ’$’ = Platzhalter fĂŒr Zeichen
inputstr = Variable mit dem eingegebenen Ergebnis (string)

Die Routine ist unter ST-Pascal von CCD lauffĂ€hig und dient als ErgĂ€nzung der PASCAL’schen Standard-Eingabebefehle. Sie lĂ€ĂŸt sich leicht in eigene Programme einbauen oder sogar als Include-Datei per Compileranweisung automatisch einfĂŒgen (z. B. $I Eingabe). Dabei sollte man allerdings folgendes beachten: Am Programmanfang mĂŒssen einige, zum Funktionieren unbedingt erforderliche Deklarationen erfolgen. Das sind der Typ ’chmenge’ und die Boolsche Variable ’ftaste’, ohne die die Routine nicht lĂ€uft.

Doch nun zu der aus zwei Teilen be-stehendenRoutine: Die Funktion ’GetChar’ wird von der Hauptroutine aufgerufen, ist aber auch getrennt von ihr gut einsetzbar. Deswegen wurde sie auch nicht lokal zur eigentlichen Inputroutine deklariert.

Sie nutzt einen GEMDOS-Aufruf, um auf einen Tastaturdruck ohne Bildschirmecho zu warten. Dieser Aufruf besteht aus zwei Wörtern (32 bit). Im zweiten Wort wird der ASCII-Code der gedrĂŒckten Taste zurĂŒckgegeben, im ersten ein besonderer Tastaturcode (SCAN-Code), der dann von Bedeutung ist, wenn eine Funktionstaste oder eine Taste aus dem. Pfeilblock gedrĂŒckt wurde. In diesem Fall ist der zurĂŒckgegebene ASCII-Code nĂ€mlich, unabhĂ€ngig von der betĂ€tigten Funktionstaste, immer Null.

FĂŒr den Fall, daß eine solche Taste gedrĂŒckt wurde, gibt die Routine den Tastaturcode als Char-Wert zurĂŒck und setzt zur Unterscheidung von einem ASCII-Code die Variable ’ftaste’ auf true. Ansonsten wird ’ftaste’ auf false gesetzt, und die Routine gibt als Ergebnis das Zeichen zurĂŒck.

Die Prozedur ’input’ ist der Hauptteil der Routine. Sie verlangt insgesamt sieben Übergabeparameter. Die ersten drei davon geben die Startposition auf dem Bildschirm sowie die erlaubte FĂ€nge der Eingabe an, wobei die LĂ€nge 80 Zeichen nicht ĂŒberschreiten sollte, da ST-Pascal sonst mit einem STRING-OVERFLOW-Error antwortet. Außerdem sollte die Eingabe nur in einer Zeile stattfinden.

Der Parameter ’default’ gibt einen Standardstring an, der als Eingabevorgabe eingeblendet wird. Somit kann diese Funktion auch einen momentanen Variableninhalt Ă€ndern. Der alte Wert wird ĂŒber ’default’ angezeigt und dann nach Belieben editiert. Dies ist bei einer Dateiverwaltung ein Ă€ußerst nĂŒtzliches Hilfsmittel.

Der alte Wert kann wĂ€hrend einer Eingabe durch DrĂŒcken von Ctrl-C bzw. UNDO wiederhergestellt werden.

Der fĂŒnfte Parameter 'erlaubt’ ĂŒbergibt in einem ’set of char’ alle Zeichen, die die Prozedur als Eingabe akzeptiert. Alle anderen Zeichen werden durch ein Signal zurĂŒckgewiesen. Dadurch kann man beispielsweise nur Zahlen oder bestimmte Buchstaben zulassen.

Der sechste Parameter ’Formatstring’ ĂŒbergibt der Prozedur einen String, in dem eine Eingabemaske festgelegt wird, in der bestimmte Zeichen nicht ĂŒberschrieben werden können (Ă€hnlich wie z. B. beim Kontrollfeld die Eingabemasken fĂŒr Datum und Uhrzeit). Dieser Formatstring kann ruhig ein bißchen schludrig oder als Leerzeichen (”) ĂŒbergeben werden. Ist die Anzahl der erlaubten Eingabepositionen im Formatstring kleiner als die mit MaxlĂ€nge angegebene Anzahl, so fĂŒgt das Programm automatisch die entsprechende Anzahl von Hintergrundzeichen ein, und zwar sucht es das erste im Formatstring vorkommende Hintergrundzeichen. Welche Stellen im Formatstring als Hintergrundzeichen interpretiert werden, also ĂŒberschrieben werden können, wird in der Variable ’editierbar’ festgelegt. Diese Variable habe ich hier nicht als Übergabeparameter vorgesehen, dies ist aber ohne weiteres möglich. Der letzte Parameter ’inputstr’ liefert den Funktionswert der INPUT-Routine. Dabei werden jedoch nur die tatsĂ€chlich eingegebenen Zeichen zurĂŒckgegeben, die Formatzeichen aus dem Formatstring werden selbstverstĂ€ndlich ignoriert.

In der Prozedur ’input’ sind noch einige weitere Prozeduren enthalten, die kleine Teilaufgaben erfĂŒllen und durch die modulare Schreibweise leicht nachtrĂ€glich zu Ă€ndern sind. So bewegt die Prozedur ’GoPos’ den Cursor an die aktuelle Cursorposition (in der Variable ’curpos’ abgespeichert). Die Prozedur 'Ausgabe' baut die Eingabemaske samt der bisher durchgefĂŒhrten Eingabe neu auf, was nach jeder akzeptierten Eingabe, die den String verĂ€ndert, geschieht. Dies ist notwendig, da der Formatstring und die tatsĂ€chliche Eingabe, die in der Variablen ’inputstr’ gespeichert ist, getrennt verwaltet werden.

Aus diesem Grunde werden auch die Cursorposition im Formatstring (’curpos’) und die tatsĂ€chliche Eingabeposition im Eingabestring (’strpos’) getrennt verwaltet. Mit den Prozeduren ’CursorLinks’ und ’CursorRechts’ wird die neue Cursorposition in der Eingabemaske errechnet. Die tatsĂ€chliche Bewegung muß mit ’GoPos’ bzw. 'Ausgabe’ erfolgen. Außerdem muß das tatsĂ€chliche Verndern der Schreibposition im Eingabestring separat erfolgen. Die Prozedur ’CharRead’ ist die Einleseroutine von der Tastatur, die Zeichen nur dann akzeptiert, wenn sie eine Kontrollsequenz sind (= ASCII 1-27 und 127), der letzte Tastendruck eine Funktionstaste war oder die Eingabe in dem durch 'erlaubt' vorgegebenen Buchstabenbereich liegt. In der Hauptroutine werden zunĂ€chst einmal verschiedene Initialisierungen vorgenommen. So wird der Überschreibmodus (Variable ’replace’) ausgeschaltet, der Formatstring wird ĂŒberprĂŒft und gegebenenfalls an die tatsĂ€chlich erlaubte LĂ€nge der Eingabe angepaßt, dem Eingabestring wird der Defaultwert zugewiesen, dieser Wert wird ausgegeben und der Cursor an das Stringende gesetzt.

Die Eingabeschleife selbst besteht aus zwei Teilen, wobei der erste die Kontrollsequenzen und die Funktionstasten abarbeitet, der zweite die Zeicheneingaben erledigt. Die Kommandos und Operationen im ersten Teil sind praktisch selbsterklĂ€rend. Etwas sehr interessantes ist dabei anzumerken: Es war möglich, Tastaturcodes fĂŒr die Funktionstasten und ASCII-Codes fĂŒr die Kontrollsequenzen zusammen abzuarbeiten, da sich die Zahlenbereiche nicht ĂŒberschneiden. So gibt z. B. bei der zweiten Option in der case-Schleife die Zahl 3 den ASCII-Code fĂŒr Ctrl-C wieder und die 97 den Tastaturcode fĂŒr UNDO.

Am Ende des Hauptprogramms sind noch einige Beispiele fĂŒr die Verwendung dieser Routine gegeben, wobei mit diesen Beispielen schon die verschiedensten Anwendungen möglich sind, z. B. Datumseingabe, Eingabe eines Dateinamens oder Eingabe bzw. Ändern verschiedener DatensĂ€tze. Den Ideen sind hierbei sicherlich keine Grenzen gesetzt. Überlegen Sie doch selbst an welchen Stellen Ihrer eigenen Programme diese Routine einsetzbar wĂ€re.

Viel Erfolg!

Bernhard Kohlhaas

{ Diese Routine wurde geschrieben vcn: Bernhard Kohlhaas Talstrasse 17 5130 Geilenkirchen } program inputroutine; {$r+} {meldet STRING-OVERFLOOWS, die bei der Verwendung von ’concat' auf strings mit einer Laenge groesser 80 auftreten} type chmenge = set of char; {notwendig fuer Inputroutine} var s : string; {hier nur fuer Beispiele notwendig} ch : char; {................. ” " ) ftaste :boolean; {notwendig fuer Inputroutine} procedure clrscr; begin write(chr(27),'E') end; procedure GotoXY(x,y:integer); begin if x<0 then x:=0 else if x>79 then x:=79; if y<0 then y:=0 else if y>24 then y:=24; writetchr(27),'Y',chr(y+32),chr(x+32)) end; procedure CursorAn; begin write(chr(27),'e') end; procedure Cursor Aus; begin write(chr(27),'f') end; function GetChar:char; const teiler = 65536; var erg :long_integer; high,low : integer; function gchar:long_integer;gemdos(8); begin erg:=gchar; high:=int(erg div teiler); low :=int(erg mod teiler); ftaste:=low=0; if ftaste then GetChar:=chr(high) else GetChar:=chr(low) end; procedure input (xpos,ypos,maxlaenge :byte; default :string; erlaubt :chmenge; Formatstring :string; var inputstr :string); { Im Hauptdeklarationsteil sind folgende Typen bzw. Variablen zu deklarieren: type chmenge = set of char; var ftaste : boolean; Ist ’true', wenn letzter Tastendruck eine Funktionstaste war } const Standardformat='_'; { Dieses Standardzeichen wird genommen, wenn im Formatstring kein anderes gewaehlt wird. } var strpos,curpos, i,nr,Formatlaenge :byte; eing,backgroundchar :char; editierbar,fbereich :chmenge; replace : boolean; procedure GoPos; begin GotoXY(xpos+curpos-1,ypos); end; procedure Ausgabe; var i,stelle :byte; begin CursorAus; GotoXY (xpos,ypos); i:=0; for stelle:=1 to length(inputstr) do begin i:=succ(i); if Formatstring[i] in editierbar then write(inputstr[stelle]) else begin repeat write(Formatstring[i]); i:=succ(i); until Formatstring[i] in editierbar; i:=pred(i); stelle:=pred(stelle); end; end; if i<Formatlaenge then write(copy(Formatstring,i+1,Formatlaenge-i)); GoPos; CursorAn; end; procedure Beep; begin write(chr(7)) end; procedure CursorRechts; {errechnet nur Cursorposition auf Bildschirm} begin {erfordert Neuberechnung von 'strpos' sowie } repeat {Ausgabe mit GoPos oder 'Ausgabe'} curpos:=succ(curpos) until (curpos>length(inputstr)+nr) or (Formatstring[curpos] in editierbar) end; procedure CursorLinks; {errechnet nur Cursorposition auf Bildschirm} begin {erfordert Neuberechnung von 'strpos' sowie } repeat {Ausgabe mit GoPos oder 'Ausgabe'} curpos: =pred(curpos) until (curpos=1) or (Formatstring[curpos] in editierbar) end; procedure Stringanfang; { bewegt Cursor zum Stringanfang } begin strpos:=1; curpos:=1; if not (Formatstring[1] in editierbar) then CursorRechts; ausgabe end; procedure Stringende; {bewegt Cursor zum Stringende} begin Stringanfang; while strpos<=length(inputstr) do begin strpos:=succ(strpos); CursorRechts end; Ausgabe end; functicn CharReadrchar; var ch :char; sound :boolean; begin sound:=false; repeat if sound then beep; sound:=true; ch:=GetChar; until (ch in (erlaubt+fbereich)) or ftaste; CharRead:=ch end; begin {Routine Input} replace:=false; editierbar:=['_','$']; fbereich:=[chr(1)..chr(27),chr(127)]; nr:=0; if length(Formatstring) =0 then Formatstring:=Standardformat; for i:=1 to length(Formatstring) do if not(Formatstring[i] in editierbar) then nr:=succ(nr); i:=1; loop exit if (Formatstring[i] in editierbar) or (i>length(Formatstring)); i:=succ(i); if i>(maxlaenge+nr) then Formatstring:=concat (Formatstring,Standardformat); end; if i>length (Formatstring) then backgroundchar:=Standardformat else backgroundchar:=Formatstring[i]; while (maxlaenge>length(Formatstring)-nr) do Formatstring:=ccncat (Formatstring,backgroundchar); Formatlaenge:=length(Formatstring); inputstr:=default; Ausgabe; Stringende; repeat eing:=CharRead; if ftaste or (eing in fbereich) then case ord(eing) of 1 : {Ctrl-A: An Stringanfang gehen} Stringanfang; 3,97 : {Ctrl-C oder UNDO: Defaultstring wiederherstellen} begin inputstr:=default; Stringende; end; 6 : {Ctrl-F: An Stringende gehen} Stringende; 8 : {Backspace: Zeichen links van Cursor loeschen} if strpos>1 then begin strpos:=pred(strpos); delete(inputstr,strpos,1); CursorLinks; Ausgabe end else beep; 22,82 : {Ctrl-V oder Insert: schaltet Einfuegencdus um} replace:=not replace; 25,27,71 : {Ctrl-Y oder ESC oder ClrHome: loescht Inputstring} begin inputstr:=''; Stringende end; 19,75 : {Pfeil links oder Ctrl-S} if strpos>1 then begin strpos:=pred(strpos); CursorLinks; Ausgabe; end else beep; 4,77 : {Pfeil rechts oder Ctrl-D} if strpos<=length(inputstr) then begin strpos:=succ(strpos); CursorRechts; ausgabe; end else beep; 7,127 : {Delete Character under Cursor} if strpos<=length(inputstr) then begin delete(inputstr,strpos,1); ausgabe; end else beep; end {case} else begin if ((eing in erlaubt) and (length(inputstr)<maxlaenge) or (replace and (strpos<=maxlaenge)) ) then begin CursorAus; if strpos>length(inputstr) then inputstr:=concat(inputstr,eing) else if replace then inputstr[strpos]:=eing else insert(eing,inputstr,strpos); strpos:=succ(strpos); CursorRechts; Ausgabe; end else beep end; until eing=chr(13) {Abschluss der Eingabe mit RETURN} end; {Im Hauptprogramm sind einige Beispiele fuer die Anwendung der Routine zu sehen.} begin clrscr; input(2,10,10,'',[' '..'z'],'_',s); clrscr; writeln;writeln; writeln(s); input(2,10,40,'Teststring',[' '..'z'],'',s); clrscr; writeln;writeln; writeln(s); input(3,10,6,'040486',['Q'..'9'],'___.__.',s); clrscr; writeln;writeln; writeln(s); input(2,10,8,'TEST', ['A'..'z','0'..'9'],'File: ________.TXT',s); clrscr; writeln;writeln; writeln(s); input(3,10,12,'A123bst4042n',[' '..'z'],'_/___/___-$$$$/',s); clrscr; writeln;writeln; writeln(s); end.