Wir verfügen über einen einfachen, aber massgeschneiderten Window-Manager. Es lassen sich mit ihm alle wichtigen Elemente wie Text, Tasten und Variable in einem Fenster definieren, aber auch Verknüpfungen der Elemente untereinander. Zur Handhabung eines Fensters dient die Klasse Fenster. Als Unterklasse eines Rechteck-Speichers, verhält es sich nach aussen wie ein Rechteck und verfügt auch über die entsprechenden Methoden (zeigen, Ursprung, Ausdehnung usw.).
Die Klasse der Fenster garantiert uns die folgenden drei Eigenschaften von GRADESS:
die Fenster sind dynamisch konfigurierbar und können auch während des Programmablaufes in ihrem Aufbau verändert werden, da der Inhalt der Fenster in der Datenbank abgespeichert wird,
die Fenster sind einfach und objektorientiert konfigurierbar, insbesondere können die Reaktionen, die von den einzelnen Elementen des Fensters ausgelöst werden sollen, als Objekte mitgegeben werden,
Mehrsprachigkeit ist durch ein Wörterbuch unterstützt, das leicht um eine weitere Sprache erweitert werden kann.
Die Elemente, aus denen der Fensterinhalt aufgebaut ist, werden mit Hilfe des Datenbank-Managers [2] abgespeichert. Bei ihrer Definition werden sie einer lokalen Kette (code genannt) angefügt. Sie haben alle sowohl Koordinaten als auch eine Ausdehnung. Sind einmal alle Elemente des Panels definiert, so wird die Grösse des Fensters berechnet, und es kann anschliessend angezeigt werden. Die Elemente lassen sich mit den Methoden addxxx dem Fenster mitteilen, wobei verschiedene weitere Parameter, je nach Typ des Elementes, mitgeliefert werden müssen.
Zur Steuerung eines gedachten Cursors (mit x- und y-Koordinaten innerhalb des Fensters) während der Definition dienen folgende Methoden:
neueZeileAnfang: yPos bringt den Cursor an den Anfang einer neuen Zeile, d.h. an den linken Rand (die x-Position 3) und an die y-Position yPos; (0/0) ist in der linken oberen Ecke.
neueZeile: yOffset bringt den Cursor auch an den Anfang einer neuen Zeile, jedoch wird zur aktuellen y-Position des Cursors der Wert yOffset dazugezählt, zusätzlich zur y-Ausdehnung des Gliedes, das zuletzt definiert worden ist. Wird die Methode mehrmals hintereinander aufgerufen, so wird nur beim ersten Mal die erwähnte y-Ausdehnung addiert.
gliedFlaeche übermittelt die eingenommene Fläche des zuletzt definierten Gliedes.
glied übermittelt das zuletzt definierte Glied selbst.
nachRechts: xOffset fährt den Cursor um den Wert xOffset nach rechts, zusätzlich zur x-Ausdehnung des Gliedes, das zuletzt definiert worden ist.
nachUnten: yOffset addiert schlicht den Wert yOffset zur aktuellen y-Position.
nachOben: yOffset tut dies nach oben (subtrahiert also den Parameter).
ox und oy übermitteln die derzeitige Position des Cursors innerhalb des Fensters, das gerade definiert wird.
ox: x und oy: y legen die aktuelle Position fest.
Dadurch, dass der Inhalt eines Fensters in der Datenbank abgespeichert wird, hat man die Möglichkeit, dynamisch die Fenster zu konfigurieren und zu verändern. Weil die Einträge in der Datenbank Objekte sind, können auch die Reaktionen, die von den einzelnen Elementen des Fensters ausgelöst werden sollen, als Objekte mitgegeben werden, was eine sehr elegante Programmierung erlaubt. Die dynamische Konfiguration erlaubt es weiter, GRADESS in einer beliebigen Landessprache zu initialisieren, ohne dass der Programm-Code neu generiert werden müsste.
Um GRADESS in verschiedenen Landessprachen betreiben zu können, sind alle seine Texte, die auf dem Bildschirm erscheinen können, parametrisiert. Das bedeutet, dass ein Text über einen Schlüssel (eine Identifikation) angesprochen wird und anschliessend in einem Wörterbuch der Ausdruck in der momentan gültigen Sprache herausgesucht wird.
GRADESS muss deshalb, bevor er zum ersten Mal gestartet werden kann, in der gewünschten Sprache konfiguriert werden. Zur Zeit sind die Sprachen deutsch, englisch, französisch und spanisch möglich. Der Befehl zur Konfigurierung auf deutsch lautet beispielsweise Gradess konfiguriere: #deutsch. Der Gartenzaun vor dem Ausdruck deutsch ist wichtig.
Auf den erwähnten Befehl hin liest das Programm aus dem File mit allen vorkommenden Wörtern in allen erfassten Sprachen (das sich im Unterverzeichnis GRADESS-System mit dem Namen Sprache befindet) die Wörter der gewünschten Sprache heraus und vereinbart sie in einer globalen Variable. Sie findet man als Klassen-Variable der Klasse Gradess unter den Namen sprachKey für den Schlüssel und sprachInhalt für den Klartext.
Zur Suche des Klartextes bei gegebenem Schlüssel kann man die Methode get: suchKey der Klasse "Gradess" herbeiziehen.
Der Klassen-Name solcher Elemente beginnt immer mit den Buchstaben "Panelglied", also beispielsweise PanelgliedCheckbox. Üblicherweise wird ein solches Element vereinbart, indem dem Fenster, das es beherbergen soll, eine Meldung mit den benötigten Parametern geschickt wird. In den folgenden Abschnitten werden wir die Elemente einzeln besprechen und auf die Meldungen zu ihrer Erzeugung eingehen. Wir besprechen nur die Meldungen, welche an das beherbergende Fenster gesendet werden müssen. Dort werden die Glieder generiert, indem die Meldungen weiter an die betreffenden Klassen geschickt werden. Auf diese Meldungen wollen wir hier nicht eingehen.
Das einfachste aller Elemente; es dient zum Anzeigen von Text. Trotzdem kann es eine id besitzen (einen Namen, der vom Inhalt des Textes abweicht), um es aus der Kette der Panel-Elemente eindeutig orten zu können (wegen der Vielsprachigkeit von Gradess ist das nötig).
Ein neuer Text kann
im Klartext vereinbart werden (addTextKlar: text bei: linksOben),
in der momentan gewählten Sprache (addText: schlüssel bei: linksOben)
oder gemischt, der erste Teil in der momentan gewählten Sprache, der zweite im Klartext (addText: schlüssel mit: text bei: linksOben);
die letzte Variante dient der komfortableren Behandlung eines Doppelpunktes am Ende des Wortes beispielsweise.
Unerwarteterweise, aber wie schon erwähnt, kann einem Text eine Kennung mitgegeben werden, damit er während dem das Panel angezeigt ist, von einer aussenstehenden Routine zum Verändern veranlasst werden kann. Die Methode kann mit addTextKlar: text id: kennung aufgerufen werden.
Eine Taste ist ein Stück Text, das von einem Rechteck eingerahmt ist. Das Rechteck reagiert, wenn mit der Maus auf ihm geklickt wird und kann daraufhin eine Reaktion auslösen. Wird die Maus gedrückt, erscheint das Rechteck invertiert (weisse Schrift auf schwarzem Grund). Wiederum muss ein Text definiert werden. Eine Kennung kann definiert werden, um beispielsweise Parameter zu vereinbaren: wie den Index, um auf ein Wert in einem Array zuzugreifen.
Eine Taste verfügt über zwei Routinen, welche sie ausführen kann:
was gilt es zu tun, wenn die Taste gerade gedrückt worden ist?
was soll die Taste tun, wenn sie soeben losgelassen wird?
Diese beiden Routinen können mit druecken: routine und loslassen: routine als Programmteile definiert werden. Ruft man die Programmteile während des Programmablaufes auf, so bekommen sie als Parameter die Taste übermittelt, welche sie gerade aufruft.
Folgende Methoden stehen zur Verfügung:
addTaste: schlüssel druecken: routine zur Vereinbarung einer Taste, deren Text sprachabhängig ist,
und entsprechend addTasteKlar: text druecken: routine. Eine so definierte Taste wird auf das Drücken der Taste reagieren.
Für den anderen Fall, dass die Taste erst in dem Moment ihre Reaktion auslösen soll, da sie nicht mehr gedrückt wird, stehen die analogen Methoden
addTaste: schlüssel loslassen: routine und
addTaste: schlüssel druecken: routine zur Verfügung.
Auch einer Taste kann eine Kennung zugeordnet werden. Dies geschieht mit den entsprechenden Methoden
addTaste: schlüssel id: kennung druecken: routine,
addTasteKlar: schlüssel id: kennung druecken: routine und
addTaste: schlüssel id: kennung loslassen: routine,
addTasteKlar: schlüssel id: kennung loslassen: routine.
Ein Pfeil dient zur Auswahl einer Zeile in einer gegebenen Liste. Derjenige Eintrag, auf welchen der Pfeil zeigt, ist im Moment gültig (die Einträge sind gesondert als Texte zu vereinbaren). Der Pfeil kann nach rechts zeigen oder nach links, je nachdem, wo der erklärende Text zu finden ist. Der Pfeil lässt sich verschieben, wenn mit der Maus auf die Stelle geklickt wird, an welcher er fortan stehen soll. Er braucht auch eine Anfangsposition.
Als spezielle Parameter benötigt die Klasse die Anzahl Zeilen, über welche der Pfeil sich verschiebbar sein soll, und die Routine, die er aufrufen soll, wenn seine Position verändert worden ist (wohl um irgend eine Variable anzupassen). Die Methode lautet in ihrer vollen Länge: addPfeile: zeilen setzen: routine anfang: anfangsposition rechts: boolean.
Eine Checkbox ist ein Quadrat, in welchem ein Kreuz stehen kann oder nicht. Steht ein Kreuz in ihr, so ist die Aussage wahr, die neben dem Quadrat zu finden ist (die Aussage ist gesondert als ein Stück Text zu vereinbaren). Das Kreuz kann gesetzt und gelöscht werden, wenn innerhalb des Quadrates mit der Maus geklickt wird.
Der Checkbox muss mitgeteilt werden, wie sie zu ihrem Inhalt gelangen kann, d.h. welche Routine sie auszuführen hat um festzustellen, ob ihr Inhalt wahr oder falsch ist.
Eine Checkbox kann auf indizierte Werte zugreifen müssen (Einträge in einem Array), was sich in einem zusätzlichen Parameter beim Aufruf der Lese- oder Schreibroutinen äussert (soll sie nicht auf indizierte Werte zugreifen, so hat der Parameter den Wert "nil"). Die Methode zur Definition lautet: addCheckbox: indiziert lesen: routine setzen: routine.
Im Unterschied zu einem Text ist der angezeigte Wert einer Variable veränderlich. Ein solches Objekt muss über zwei Methoden verfügen, nämlich
lesen, um den Wert der Variablen in sein Inneres holen zu können, und
schreiben, um einen veränderten Wert wieder in die ursprüngliche Variable zurückschreiben zu können.
Weiter muss man die Breite des Feldes mitliefern, da nicht von vornherein klar ist, wie breit die angezeigte Variable werden kann. Ist die Breite zu klein, das heisst, braucht die Variable mehr Platz, als für sie vorgesehen ist, so wird über den Rand hinausgeschrieben.
Wie auch schon die Checkbox, kann eine Variable auf indizierte Werte zugreifen. Die Methoden lauten somit:
addVariable: maxLänge lesen: routine setzen: routine in der einfachsten Form und
addVariable: maxLänge indiziert: boolean lesen: routine setzen: routine für indizierten Zugriff.
Wie auch schon bei früher behandelten Elementen lässt sich auch hier einer Variable eine Kennung zuteilen. Und so verlängern sich die Methoden um den Parameter "id":
addVariable: maxLänge lesen: routine setzen: routine id: kennung und
addVariable: maxLänge indiziert: boolean lesen: routine setzen: routine id: kennung.
Sie verläuft vom linken zum rechten Rand des Fensters, egal wie breit es ist. Zur Definition genügt deshalb die Angabe der y-Koordinate. Ein weiterer Parameter muss aber übergeben werden, welcher den Abstand in y-Richtung angibt, der nach der Linie angefügt werden soll. Die Methode lautet: addQuerlinie: leerraum.
Eigentlich gehört ein Punkt nicht auf ein normales Eingabe-Panel. Jedoch ist er dann nötig, wenn die innere Topologie einer Weiche festgelegt werden soll (welche Fahrstrecke mit welcher zu verbinden ist). Dann müssen Linien zwischen Punkte gelegt werden können. Dafür gibt es die Methode addPunkt ohne weiteren Parameter, welche an die Position des Cursors innerhalb des Panels einen Punkt definiert. Und folglich auch die Methode addLinie: anfang ende: ende, welche eine Linie (die a priori im Fenster definiert sei) zwischen die beiden Punkte legt, die als Parameter übermittelt werden.
Zur Anzeige eines Menubalkens dient die Klasse FensterMenubalken, eine Unterklasse von Fenster. Von sich aus erwartet es Menu-Punkte (einzelne Wörter) und die Definition von Untermenus, welche beim Aufruf des Menu-Punktes zur Auswahl angezeigt werden sollen.
Sequenziell werden die Menu-Punkte aneinandergereiht, wenn dem Menubalken die Meldung baueBalken: wort unterbegriffe: liste geschickt wird. Der Parameter "Wort" enthält den Begriff, der im Menubalken erscheinen soll, und die "Liste" alle Unterbegriffe des zugehörigen Untermenus. Die "Liste" ist ein String, und nach jedem Begriff des Untermenus hat ein senkrechter Strich ("|") zu erscheinen, auch nach dem letzten, um sie voneinander zu trennen.
2.4.2 Fenster für die UntermenusSoeben erwähnten wir, dass das Fenster für den Menubalken Unterfenster definiert, und zwar für jeden Menu-Punkt. Dies geschieht durch die Zuordnung eines FensterUntermenus zu jedem Menu-Punkt, das seinerseits aus Elementen der Klasse PanelgliedMenupunkt (einer modifizierten Taste, ohne Rahmen) aufgebaut ist. Zur Definition des Untermenus schliesslich dient die Methode, welche für alle Klassen der Abstammung Fenster definiert ist, mit dem Namen addMenupunkt: schlüssel fenster: fenster. Die Methode erwartet den Text des Menu-Punktes (parametriesiert für Mehrsprachigkeit) und das Fenster, in welchem das Menu angezeigt werden soll.