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.