Alle Macht den Tasten - Formatierte Eingaberoutine für PASCAL

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.


Aus: ST-Computer 03 / 1987, Seite 116

Links

Copyright-Bestimmungen: siehe Über diese Seite