FoxPro

FoxPro Developer's Conference '94

Session 231
GENSCRNX & GENMENUX

Peter Herzog


 

EINFÜHRUNG

Eine Einführung in die Funktionsweise der Powertools für FoxPro 2.x.

GENSCRNX und GENMENUX sind zwei Programme, welche die Funktionalität des Masken- und des Menügenerators erheblich verbessern und erweitern.

GENSCRNX wurde von Ken Levy bei JPL (Jet Propulsion Laboratories) bei der NASA entwickelt und als Public Domain der Öffentlichkeit zur Verfügung gestellt. Allein schon die freie Verfügbarkeit und Verwendbarkeit haben dieses Tool überall bekannt und berühmt gemacht.

GENMENUX wurde von Andrew R. MacNeill aus Kanada entwickelt. Es basiert auf demselben Prinzip wie GENSCRNX und wurde ebenfalls als Public Domain freigegeben.


 

GENSCRNX:

Die Funktionalität und Arbeitsweise kann man in drei Sparten unterteilen:

  1. Erweiterung der Befehle des Maskeneditors
  2. Verwendung einer Maskenlibrary
  3. Einbindung von eigenen Treibern um die Funktionalität nochmals zu erweitern.

Um die Arbeitsweise zu verstehen, muß man wissen, wie eine Maske unter FoxPro gestaltet werden kann.

Grundsätzlich kann man eine Maske „zu Fuß" erstellen, indem man die einzelnen @ Say und @ Get Anweisungen in ein Programm schreibt. Dies war unter DOS noch vertretbar, spätestens jedoch unter Windows, wurden die Bildschirmpositionen nach dem Komma dreistellig und es war kaum noch möglich, schöne, ansprechende Bildschirmmasken zu gestalten, ohne enorm viel Zeit beim Ausprobieren zu verschwenden.

FoxPro hat uns, zum Glück, bereits in früheren Versionen des Produktes einen Maskeneditor zur Verfügung gestellt, mit dem man auf einfachste Art und Weise im WYSIWIG (What You See Is What You Get) Verfahren, Masken gestalten konnte.

Nicht nur die Gestaltung der Maske war möglich, sondern auch das Schreiben der verschiedenen Klauseln eines Feldes wurde erleichtert. Durch einen einfachen Doppelklick auf das Feld am Bildschirm erscheint eine Auswahlbox, in der man sowohl die verschiedensten Einstellungen tätigen kann, als auch die einzelnen Snippets (zu dt. Schnipsel, Programmteile) bearbeiten kann.

Wenn nun die Maske fertig zusammengestellt ist, kann man durch den Programmpunkt GENERIEREN die Maske in ein lauffähiges Programm umwandeln. Anders als die Programme tragen diese Maskendateien die Endung „.SPR".

Beim Menüpunkt GENERIEREN startet FoxPro ein Programm, dessen Name in einer Variable namens „_genscrn" gespeichert ist. Dieses Programm (normalerweise „GENSCRN.PRG") ist im Lieferumfang von FoxPro enthalten und verwandelt die Maskendefinition mit Layout, Farbe, Größe, usw. in reinen FoxPro-Code.

Leider haben die FoxPro-Programmierer nicht alle Befehlselemente in den Maskeneditor untergebracht, wodurch wir nicht alles machen konnten, was FoxPro als Programmiersprache zur Verfügung stellt. (Bsp.: die Klausel DEFAULT)

Dies hat natürlich viele Programmierer dazu verleitet, sich eigene Generatoren zu schreiben, bzw. das bestehende GENSCRN.PRG zu verändern. Der Sourcecode liegt ja im Home - Verzeichnis von FoxPro und kann leicht verändert werden.

Damit fingen die Probleme erst richtig an. So wie in der Computerindustrie alle verschiedenen Hersteller eigene Standards erzeugen wollen, so gab es nun die verschiedensten GENSCRN.PRG's. Das eine konnte die Aufgabe A erledigen, das andere die Aufgabe B. Wollte man nun beide Funktionalitäten in einem Produkt haben, so mußte man selbst den GENSCRN.PRG wieder verändern. Viele Anbieter von Zusatztools standen vor diesem Problem.

Die ganze Prozedur des Änderns wiederholte sich jedesmal, wenn FoxPro eine neue Version des GENSCRN herausbrachte. Wehe, wenn dann die eigenen Änderungen nicht fein säuberlichst dokumentiert waren.

Ein weiteres Problem war die Starrheit von GENSCRN.PRG. Man konnte nur oben etwas hineinschicken und bekam unten ein Programm heraus. Alles was dazwischen lag, konnte man nicht beeinflussen. (Kleine Ausnahmen wie #NAME, #ITSEXPRESSION usw.)

Leider wird mit dem Befehl „PRIVAT ALL" jeder Eingriff von Außen zunichte gemacht.

Mit GENSCRNX.PRG konnte man alle Probleme elegant umschiffen. Die Idee liegt darin, dem eigentlichen GENSCRN.PRG bereits vorgekaute „Nahrung" zu liefern, und das Endprodukt nachträglich noch zu verändern. Dies alles wird vollautomatisch durch Befehle gesteuert.

Durch die Systemvariable _genscrn kann man steuern, welches Programm gestartet werden soll, wenn man den Menüpunkt GENERIEREN anwählt. Normalerweise würde dies GENSCRN.PRG sein. In unserem Falle allerdings wird eben GENSCRNX.PRG aufgerufen. GENSCRNX legt sich eine Kopie der Maskendatei an und durchsucht nun diese Kopie nach Befehlen, die mit Stern Doppelpunkt „*:" beginnen.

Werden solche Befehle gefunden, so verändert GENSCRNX die Maskendatei. Auf diese Art und Weise wurde z.B. der Befehl *:DELETE realisiert. Da die Maskendatei in Wirklichkeit eine normale DBF ist (nur mit der Endung SCX) kann man auf gewohnte Art und Weise darauf zugreifen. Im Falle des *:DELETE wird einfach der Datensatz gelöscht.

Für jedes Objekt in der Maske gibt es einen Datensatz. Durch spezielle Nummern (5 ist ein SAY-, 15 ist ein GET-Objekt) kann GENSCRNX feststellen, womit es zu tun hat.

Es sei noch erwähnt, daß die GENSCRNX-Befehle (mit wenigen Ausnahmen) in das Kommentarfeld geschrieben werden. Nun hat auch dies einen Sinn und kann verwendet werden.


 

1. Erweiterung der Befehle des Maskeneditors

Einige Befehlsbeispiele seien hier aufgeführt:

*:IF <logischer Ausdruck>

Jedes Objekt kann in ein IF ENDIF eingekreist werden. Abhängig vom logischen Ausdruck wird das Objekt nun angezeigt oder nicht.

*:PRG

Die gesamte Maske wird automatisch als PRG (anstelle von SPR) abgespeichert.

*:SET <Ausdruck> ON

Mit den SET-Befehlen kann man steuern, ob zum Beispiel der Code zum Öffnen von Dateien oder zum Definieren der Fenster generiert werden soll, oder ob z.B. die Maske modal werden soll usw. (SET MODAL ON)

*:DELETE

Löscht ein Objekt aus der Maskendatei, bevor es generiert wird.

*:DELOBJ

Löscht ebenfalls ein Objekt aus der Maskendatei. Jedoch werden zuerst die Snippets auf GENSCRNX-Kommandos durchsucht und eventuell evaluiert.

*:VISIBLE <logischer Ausdruck>

Erzeugt automatisch Code im Programm, welcher abhängig von einem logischen Ausdruck das Objekt erscheinen oder verschwinden läßt.

*:BUTTON <Funktionsname>

Dieser Befehl fügt eine unsichtbare Schaltfläche mit einer VALID-Klausel hinter SAY, Text, BOX und Picture-Elementen ein.

*:DISABLE <logischer Ausdruck> *:ENABLE <logischer Ausdruck>

Diese Befehle fügen eine spezielle IF-Konstruktion in die SHOW-Klausel des Reads ein, um das Objekt abhängig vom logischen Ausdruck zu enablen oder zu disablen.

*:INSTXT *:ENDTEXT

Ein zwischen diesen Befehlen geschriebener Programmcode wird anstelle des Objektes eingefügt.

*:SIZE <Ausdruck>

Ersetzt die SIZE-Klausel mit dem angegebenen Ausdruck.

*:FUNCTION <Funktion> *:ENDFNCT

Ein zwischen diesen Befehlen geschriebener Programmcode wird automatisch in das Cleanup-Snippet der Maske eingetragen. Dadurch kann ein Library-Objekt seinen eigenen Programmcode mitvererben. Wenn der Funktionsausdruck mehrmals in der Maske vorkommt, wird selbstverständlich nur der Erste verwendet.

 

{{...}}

Mit Hilfe der geschweiften Klammern kann ein beliebiger Audruck evaluiert werden. Folgender Befehl

? „das heutige Datum lautet {{date()}}"

läßt den {{date()}} Ausdruck zur Generationszeit evaluieren und fügt das aktuelle Datum an genau dieser Stelle in den Code ein.

{{< Dateiname}}

Mit diesem Befehl können Sie an genau dieser Stelle eine beliebige Datei ,einfließen" lassen. Achten Sie auf das „Kleiner"-Zeichen nach der geschweiften Klammer.

In der angegebenen Datei können natürlich wieder andere Kommandos stehen, die dann wiederum ausgeführt werden.

Die Liste kann nun endlos weitergeführt werden. Es sei hier auf das Sonderheft GENSCRNX/GENMENUX der dFPUG oder auf das auf der beiliegenden Diskette gespeicherte WRI-File verwiesen, in dem alle Befehle aufgelistet sind.


 

2. Verwendung einer Maskenlibrary

Da es sich bei der Maskendatei um eine normale DBF handelt, ist es natürlich ein Leichtes, zusätzliche Elemente einzufügen. Damit man dies nun nicht selbst machen muß, liefert uns GENSCRNX zahlreiche Befehle, um dies zu bewerkstelligen.

Sobald GENSCRNX das erste Mal gestartet wird, sucht es eine Datei mit dem Namen FOXSCX.DBF. Sollte diese Datei nicht vorhanden sein, so wird sie automatisch angelegt.

In dieser Datei können nun Objekte und Masken abgelegt werden.

Man spricht hier von Libraries, also von Bibliotheken. Eine Bibliothek entspricht einer Maske, welche man ganz normal mit dem Maskeneditor erstellt.

Der Unterschied zu normalen Masken besteht darin, daß man in die Initialisierungsprozedur das Befehlswort *:DEFLIB <library> schreibt.

Durch dieses Befehlswort erkennt GENSCRNX automatisch, daß er keine Maske erstellen soll, sondern die Maskenlibrary FOXSCX.DBF füllen soll.

Der Name der Library wird nach dem Befehlswort *:DEFLIB angeführt.

Nun müssen noch die einzelnen Objekte benannt werden. Dies geschieht mit dem Befehlswort *:DEFOBJ <Objektname>. Diesen Befehl schreibt man in das Kommentarsnippet.

Wenn nun aus dem Menü der Eintrag GENERIEREN gewählt wird, so wird jedes Element, welches den Befehl *:DEFOBJ beinhaltet, in die FOXSCX aufgenommen bzw. neu hineingeschrieben. Es sei anzumerken, daß alle Angaben zum Objekt mit Ausnahme seiner Position in die Library aufgenommen werden, auch die verschiedenen Snippets.

In einem Kommentarsnippet kann nun wieder ein Verweis auf ein anderes Objekt stehen. In diesem wieder ein Verweis usw. Dies kann beliebig oft wiederholt werden.

Nun stehen uns die Objekte zur Verfügung. Bei den Beispielen von GENSCRNX sind bereits zwei Maskenlibraries beigefügt. (LIBMAIN1.SCX, LIBACCT1.SCX)

In unserer Zielmaske kann nun auf die in der Library gespeicherten Objekte zugegriffen werden.

Am einfachsten geht dies durch den Befehl *:BASLIB <Library>.

In diesem Falle wird bei jedem Feld nachgesehen, ob der Name des Objektes (Variablenname) in der bezeichneten Library vorkommt. Wenn dies der Fall ist, werden automatisch Aussehen, Farbe, Font und Code-Snippets aus der Library geholt und dem Zielobjekt zugefügt.

Dies ist der einfachste, Weg um Standardmasken mit Leben zu füllen, da auch die When und Valid Routinen mit übertragen werden.

Eine andere Möglichkeit, ist die explizite Referenzierung auf ein Objekt in der Library. Dies geschieht durch den Befehl *:BASOBJ <Objektname> im Kommentarsnippet, wobei beim Objektnamen auch die Library angegeben werden kann. (*:BASOBJ MAIN.Pushbutton_Record)

Auch in diesem Falle werden alle Attribute usw. übernommen. Sollte man selbst bereits Programmcode in die einzelnen Snippets geschrieben haben, so wird der zugefügte Code hinten angehängt. Wenn man den Code jedoch vor seinem Eigenem finden möchte, so muß der Befehl *:BASEBEFORE angewendet werden.

Noch eine Möglichkeit, ist die Übernahme eines Objektes durch Ersetzen. Dies geschieht durch den Befehl *:INSOBJ <Objektname>. Hier wird das ursprüngliche Objekt völlig gelöscht und an seiner Stelle das neue eingefügt.

Da diese Art des Objektkopierens so einfach ist, wurde auch noch die Möglichkeit geschaffen, ganze Masken hereinzuholen. (*:INSSCX)


 

3. Einbindung von eigenen Treibern um die Funktionalität nochmals zu erweitern.

Beim Erfinden eines neuen Tools wird es immer wieder Programmierer geben, die ihre eigenen Ideen in dieses Tool einfließen lassen wollen.

Um nun nicht immer diese neuen Ideen nachprogrammieren zu müssen, ermöglichte Ken Levy die Einbindung von eigenen Treibern.

Mittlerweile sind eine Vielzahl von solchen Treibern veröffentlicht worden und bis auf wenige Ausnahmen ebenfalls Public Domain.

Als attraktivstes Beispiel dient der 3D Treiber, der Masken einen räumlichen Eindruck verschafft. Überschriften mit Schatten, versenkte Eingabefelder oder hervorgehobene Rahmen zählen zum Leistungsspektrum.

Als weiteres Beispiel darf der INTL-Treiber von Steven Black angeführt werden. Dieser ist jedoch nicht Public Domain, sondern käuflich zu erwerben.

Dafür leistet er Gewaltiges. Dieser Treiber ermöglicht eine Mehrsprachigkeit der Masken und Menüs. Die Mehrsprachigkeit kann entweder zur Laufzeit oder beim Generieren der Maske bzw. des Menüs erzeugt werden.

Bei den Treibern handelt es sich um normale FoxPro-Programme, die auf geschickte Art und Weise die SCX-Datei bearbeiten. Sie können auch selbst GENSCRNX-Befehle in die Maskensnippets schreiben, die dann wiederum von GENSCRNX ausgeführt werden.

Um einen Treiber in das System einzubinden, gibt es wieder spezielle Befehle. Wenn ein Treiber zu einer Maske eingebunden wird, dann muß man im Initialisierungsteil *:SCXDRVx <Treibername> schreiben.

Das kleine „x" gilt als Platzhalter für eine Nummer zwischen 1 und 8. In GENSCRNX gibt es acht Punkte an denen ein oder mehrere Treiber eingebunden werden können.

In der Beschreibung des Treibers oder in den ersten Zeilen des Treibers steht, unter welcher Nummer er eingetragen werden muß. Für den 3D Treiber muß z.B. die Nummer 5 angegeben werden.

Die Nummern geben einen bestimmten Verarbeitungsschritt an, bei dem GENSCRNX sich befindet.

Treiber mit der Nummer eins werden vor jeder Verarbeitung von GENSCRNX abgearbeitet. Die Treiber mit der Nummer acht werden nach jeder Verarbeitung aufgerufen, jedoch bevor die Kontrolle an den FoxPro eigenen GENSCRN.PRG übergeben wird.

Um am Endprodukt des GENSCRN.PRG noch etwas zu verändern, wird der generierte Code in ein temporäres Memofeld geholt.

GENSCRNX selbst fügt dann noch diversen Code ein. Ab hier ist es dem Programmierer möglich, selbstständig Treiber einzubinden. In diesem Falle gibt es nur noch 6 Einsprungpunkte, und der Befehl lautet *:SPRDRVx <Treibername>

Zur Zeit gibt es das RESIZE6.PRG, das ein variables Ändern der Größe einer Maske ermöglicht, wenn dieselbe Maske unter verschiedenen Bildschirmauflösungen gestartet wird.


 

GENMENUX:

So wie es für die Masken den GENSCRNX gibt, so gibt es für die Menüs den GENMENUX.

Das Prinzip ist genau dasselbe wie bei GENSCRNX. Anstelle des GENMENU.PRG von FoxPro wird das Programm GENMENUX.PRG aufgerufen, welches die Menüdatei bearbeitet.

Auch hier wird eine Kopie der Datendatei gemacht und entsprechend der Befehle verändert.

Die Befehlssyntax entspricht auch hier denen des GENSCRNX. Unter anderem gibt es auch hier ein *:IF, welches einzelne Menüpunkte abhängig von einem logischem Ausdruck anzeigt oder nicht.

Unter anderem ermöglicht GENMENUX, die Farben und die Position des Menüs zu verändern. Dies hat zwar unter Windows nicht mehr so viel Bedeutung, unter DOS hat dies jedoch immer wieder Nacharbeiten erfordert, denen ein Programmierer gerne aus dem Weg gehen möchte.

Wie bei GENSCRNX gibt es auch hier eine Library-Datei, die hier den Namen FOXMNX.DBF trägt. Aus ihr heraus können ganze Standardmenüs geholt werden.

Auch gibt es hier die Möglichkeit, Treiber einzubinden. Der dazugehörige Befehl lautet *:MNXDRVx und *:MPRDRVx, bzw. _MNXDRVx und _MPRDRVx wenn die Treiber in der CONFIG.FPx eingetragen werden.

*:MNXDRV1

ruft einen Treiber auf, der auf die Originaldatei aufsetzt. Damit unterscheidet er sich vom normalen Ablauf von GENSCRNX.

*:MNXDRV2

Der an dieser Stelle eingetragene Treiber bearbeitet nun nur noch eine Kopie der Datei. Dieser Treiber wird für jeden Datensatz einmal aufgerufen und kann somit aus einer einzelnen Zeile bestehen.

*:MNXDRV3

Dieser Treiber wird aufgerufen, nachdem das Menü neu sortiert wurde. An dieser Stelle darf kein Datensatz mehr gelöscht werden, da ansonsten die Menüdatei nicht mehr richtig funktionieren würde. Ansonsten funktioniert er wie der Treiber Nummer 2.

*:MNXDRV4

Dieser Treiber wird aufgerufen, bevor das eigentliche GENMENU.PRG aufgerufen wird. Dies ist der Punkt, an dem die meisten Treiber einwirken. Dieser Treiber muß jedoch selbst Satz für Satz der Menüdatei bearbeiten, da dieser Treiber nur einmal aufgerufen wird.

*:MNXDRV5

Wenn hier ein Treiber aufgerufen wird, so ersetzt er den GENMENU.PRG.

An dieser Stelle wird der Sourcecode erzeugt und wiederum in ein temporäres Memofeld gespeichert.

*:MPRDRV1

*:MPRDRV2

Diese beiden Treiber-Einsprungpunkte können nun noch den bereits generierten Code verändern. Diese Treiber müssen jedoch selbst dafür sorgen, jede Zeile des generierten Codes zu durchsuchen.

Nun erst wird das endgültige Produkt auf die Platte geschrieben.

Wie bereits erwähnt gibt es bei GENMENUX ebenfalls die Möglichkeit, Libraries zu erstellen.

Dies wird durch die Befehle *:DEFLIB <Library> und *:DEFOBJ <Objektname> erledigt. Die Datei, in der die Objekte abgelegt werden, wird per Voreinstellung FOXMNX.DBF genannt, ist jedoch durch das Kommando *:FOXMNX <Dateiname> umzubenennen.

Aufgerufen werden die Elemente durch die bereits beim GENSCRNX erwähnten Befehle: *:INCLIB und *:INSOBJ.

Zusätzlich gibt es noch eine Vielzahl an Befehlen, die die Funktionalität und das Aussehen eines Menüs verändern.

Beispiele:

*:AUTOHOT

Fügt allen Menüüberschriften (nicht den einzelnen Menüzeilen) HotKeys zu. GENMENUX versucht in erster Linie den Anfangsbuchstaben zu verwenden, sollte dieser Buchstabe bereits vergeben sein, so wird der Nächste versucht, usw.

*:FOUNDATION <Ausdruck>

Dieser Befehl fügt ein READ VALID <Ausdruck> in den Menücode ein. Das Menü wird dann automatisch verlassen, sobald in einem Menü entweder der Audruck ,EXIT" oder ,QUIT" verwendet wird und im <Ausdruck> nichts angegeben wird.

*:VERTICAL <Startreihe>,<Zwischenzeilen>

Mit diesem Befehl kann man ein Menü vertikal anzeigen lassen. Das bedeutet jedoch nicht, daß die Schrift auch um 90 Grad gedreht wird, sondern daß die einzelnen Menüpunkte untereinander gereiht werden. Den Abstand zwischen den einzelnen Menüpunkten kann man mit dem zweiten Parameter <Zwischenzeilen> angeben.

*:INSSCX <Maskenname> [SAVE[MODAL]]

Mit diesem Befehl wird in die angegebene Maske automatisch der Aufruf des Menüs mittels DO MENU.MPR eingefügt. Die zusätzliche Option SAVE gibt an, daß ein PUSH MENU und POP MENU in das Init- und Cleanup-Snippet eingetragen wird. Mit der Zusatzoption MODAL wird der Aufruf des Menüs in die When -Klausel des Reads eingetragen.

*:LINE <Zeile>

Mit diesem Befehl kann man angeben, in welcher Zeile man das Menü erscheinen lassen möchte.

*:LOCATION <Ausdruck>, <MenüPrompt>

Durch *:LOCATION REPLACE wird z.B. das aktuelle Menü ersetzt. <Ausdruck> kann auch noch die Werte AFTER, BEFORE und APPEND annehmen. In diesen Fällen muß im zweiten Parameter der Menüprompt angegeben werden.

*:MENUNAME <Ausdruck>

Ändert den Namen der Menüleiste von _MSYSMENU auf den angegebenen Ausdruck.

*:WINDOW <Fenstername> [CLAUSES <Ausdruck>]

Mit dieser Anweisung können Sie das Menü in einem bestimmten Fenster (mit DEFINE WINDOW definiert) erscheinen lassen. Durch die Option CLAUSES wird automatisch ein neues Fenster definiert und das Menü erscheint in diesem Fenster.

*:DELETE

Löscht einen Menüeintrag heraus.

*:DELOBJ

Löscht ebenfalls einen Menüeintrag, jedoch erst, nachdem alle zusätzlichen im Kommentarsnippet gefundenen Direktiven abgearbeitet wurden.

*:IF <logischer Ausdruck>

Klammert einen Menüpunkt in einen IF ENDIF Ausdruck ein. Durch den logischen Ausdruck kann entschieden werden, ob der Menüpunkt zur Laufzeit erscheint oder nicht.

*:COLOR und *:COLORSET

Mit Hilfe dieser Anweisungen ist es möglich, die Farbe des Menüs zu verändern.

*:MESSAGE <Zeichenkette>

Da die DOS-Version von FoxPro keine Möglichkeit bietet, die Messageklausel zu belegen, kann dies hiermit gemacht werden.

Dies war nur ein kleiner Auszug aus den möglichen Befehlen. An dieser Stelle sei wieder auf das Sonderheft der dFPUG und das bereitgestellte WRI-File auf der Begleitdiskette hingewiesen.


GenScrnX
(c)1994 Peter Herzog