Dieser Artikel erklärt die Implementierung von IntelliSense in Visual
FoxPro 7.0 und zeigt auf,
wie Entwickler sich durch die offene Architektur ihr Leben erleichtern und
ihre Produktivität steigern können.
![]() |
Konfigurieren von IntelliSense |
![]() |
Die Tabelle FoxCode |
![]() |
Erstellen und Einsetzen von Skripts |
Es gibt in Visual FoxPro 7.0 zwei Möglichkeiten, IntelliSense zu konfigurieren.
Die erste Möglichkeit besteht in der Einstellung des Werts und der neuen Eigenschaft
EditorOptions des Anwendungsobjekts von VFP. Die Vorgabeeinstellung ist "LQKR",
obwohl es zur Zeit fünf Möglichkeiten gibt, die durch diese Eigenschaft gesteuert
werden können:
• |
Hyperlinks (K oder k): entscheidet, wie Links aktiviert werden. Die Einstellung "k" erfordert lediglich einen einfachen Mausklick, während "K" ein CTRL-Click erfordert. Dies ist die Vorgabeeinstellung. Ist nichts angegeben, werden Hyperlinks im Editor und Befehls-Fenster als normaler Text betrachtet. |
• |
Word Drag 'n Drop (W): wenn eingeschaltet kann Text nur in eine Position unmittelbar hinter einem Leerzeichen verschoben werden. Dies schützt Sie vor dem unbeabsichtigten Einfügen im bestehenden Text, wenn Sie im Editor oder im Befehls-Fenster mit Drag-and-Drop arbeiten. Als Vorgabewert ist dieses Verhalten deaktiviert. |
• |
Designer Value Tips (T): damit wird gesteuert, ob Elemente, die in Listen angezeigt werden, auch ihren Tooltipp anzeigen. Als Vorgabewert werden die Tooltipps angezeigt. |
• |
List Member (L oder l): damit wird gesteuert, ob und wann die Elementlisten angezeigt werden. Als Vorgabe werden Listen automatisch angezeigt (L). Sie könnten es jedoch bevorzugen, das kleine l anzugeben, um die automatische Anzeige zu unterdrücken. Dennoch verfügen Sie dann über die Möglichkeit, die Liste mit CTRL-3 aufzurufen (oder im Menü "Bearbeiten" von FoxPro den Menüpunkt "Elemente anzeigen" zu wählen). |
• |
Quick Info (Q oder q): damit wird gesteuert, ob und wann die unterschiedlichen Arten der QuickInfos angezeigt werden. Als Vorgabewert werden sie automatisch angezeigt (Q), obwohl Sie es bevorzugen könnten, die Option "q" zu wählen, um die automatische Anzeige zu unterdrücken, aber noch über die Möglichkeit zu verfügen, das QuickInfo mit CTRL+I aufzurufen (oder im Menü "Bearbeiten" von FoxPro den Menüpunkt "QuickInfo" zu wählen). |
Um alle Features von IntelliSense zu aktivieren (einschließlich der Kontrolle des Drag-and-Drop von Text), geben Sie im Befehls-Fenster einfach die folgende Zeile ein:
_VFP.EditorOptions = "LQKTW"
Die zweite Möglichkeit besteht darin, den neuen IntelliSense-Manager einzusetzen,
den Sie über das Menü Extras -> IntelliSense-Manager aufrufen. Dieses Formular
verfügt über vier Seiten und eröffnet Ihnen nicht nur Zugriff auf die eben beschriebenen
Konfigurationsmöglichkeiten, sondern über die Tabelle FoxCode, durch die IntelliSense
gesteuert wird, auch auf andere Funktionalitäten von IntelliSense.
Die erste Seite (Abbildung 17) bietet Ihnen eine interaktive Möglichkeit, die
Eigenschaft _VFP.EditorOptions für die Elementliste und das QuickInfo einzustellen.
Dort wird auch definiert, wie IntelliSense die Groß- und Kleinschreibung für
Befehle und Funktionen behandeln soll, sowie ein Vorgabewert der immer dann
verwendet werden soll, wenn keine spezielle Anweisung vorliegt. Die Checkbox
entscheidet, ob die definierten Einstellungen nur für die nativen Befehle und
Funktionen, oder auch für benutzerdefinierte Befehle und Funktionen gelten sollen.
Schaltflächen aktivieren ein Fenster mit Tipps und zeigen den Inhalt der Tabelle
FoxCode an, in der die Informationen gespeichert sind, die von IntelliSense
genutzt werden.
Abbildung 1. Die Konfigurationsseite des IntelliSense-Managers
Die zweite Seite Typen (Abbildung 18) steuert die Einträge, die in der Typenliste angezeigt werden, wenn die Klausel AS eingesetzt wird. Visual FoxPro 7.0 wird mit einem Grundset ausgeliefert, das alle Basisklassen von Visual FoxPro, sowie alle Standard-Datentypen umfasst. Dies geschieht unabhängig davon, ob sie im Moment in Visual FoxPro verfügbar sind oder nicht. Wird eine Checkbox deaktiviert, wird dieses Element in den Typenlisten nicht mehr angezeigt.
Abbildung 2. Die Typenliste von IntelliSense
Mit den Schaltflächen dieser Seite können die verfügbaren Typen erweitert werden.
• |
Die Schaltfläche Bearbeiten öffnet ein Fenster, das die Einträge der aktuell ausgewählten Elemente in der Tabelle FoxCode anzeigt. Diese Möglichkeit wird in der Regel genutzt, um die Anzeige für ein Element zu ändern. |
• |
Die Schaltfläche Typbibliotheken öffnet einen Dialog, der die registrierten COM-Server, ActiveX-Steuerelemente oder beides auflistet und es ermöglicht, diese der Typliste hinzuzufügen. |
Abbildung 3. Der Typliste von IntelliSense eine Typbibliothek hinzufügen
• |
Die Schaltfläche Klassen öffnet Visual FoxPros Standarddialog visueller Klassen und ermöglicht es, benutzerdefinierte Klassen der Typliste von IntelliSense hinzuzufügen (Abbildung 20). |
Abbildung 4. Der Typliste von IntelliSense eine benutzerdefinierte Klasse hinzufügen
Beachten Sie, dass Klassen, die in Programmdateien definiert wurden, in die Typliste aufgenommen werden können, dass aber der entsprechende Datensatz der Tabelle FoxCode manuell hinzugefügt werden muss.
• |
Die Schaltfläche Webdienste ruft den Assistenten "Registrierung der Visual FoxPro Webdienste" auf und fügt der Typliste den angegebenen Webdienst hinzu (Abbildung 21). |
Abbildung 5. Der Typliste von IntelliSense einen Webdienst hinzufügen
Die dritte Seite des IntelliSense-Managers, Benutzerdefiniert, stellt eine filterbare Ansicht bereit, die zum Hinzufügen, Ändern oder Löschen benutzerdefinierter Einträge in der Tabelle FoxCode dient (Abbildung 22).
Abbildung 6. Der FoxCode-Tabelleneditor
Der nächste Abschnitt dieses Artikels behandelt detailliert die Tabelle FoxCode.
Die letzte Seite, Erweitert, ermöglicht Ihnen den Zugriff auf zwei zusätzliche
Elemente, "Eigenschaften bearbeiten" und "Bereinigen" (Abbildung 23). Auf diese
Eigenschaften gehen wir im nächsten Abschnitt dieses Artikels im Detail ein.
Abbildung 7. Die erweiterten Optionen des IntelliSense-Managers
Die Schaltfläche Bereinigen ruft einen Verwaltungsdialog für die Tabelle FoxCode sowie für die MRU-Listen auf (Abbildung 24).
Abbildung 8. Der Wartungsdialog für die Tabelle FoxCode und die MRU-Listen
Detaillierte Angaben zu diesem Dialog finden Sie in der Referenz von Visual FoxPro.
Diese Tabelle, die als Vorgabewert im Verzeichnis "Dokumente und Einstellungen" des Anwenders auf der lokalen Festplatte installiert wird, ist das Herz der Implementierung von IntelliSense in Visual FoxPro 7.0. Durch die Manipulation der Inhalte dieser Tabelle kann die Funktionalität von IntelliSense angepasst und erweitert werden. Daher ist es unbedingt erforderlich, dass Sie die Struktur und den Einsatz dieser Tabelle mit IntelliSense in Visual FoxPro verstehen.
Feld | Definiert als | Beschreibung |
Type |
C (1) |
Gibt an, wie der Datensatz verarbeitet werden
soll: |
Abbrev |
C (24) |
Shortcut, der eine Aktion auslöst. |
Expanded |
C (26) |
Wenn erforderlich, die expandierte Version oder die Ersetzung des Shortcut. |
Cmd |
C (15) |
Der Name des auszuführenden Skripts. In "{}" geklammert. |
Tip |
M ( 4) |
Anzeigeinformation des QuickTipp. |
Data |
M ( 4) |
Der Inhalt dieses Datensatzes kann Listenwerte, Code, Skripttext usw. enthalten. |
Case |
C ( 1) |
Gibt an, in welchem Format der Text ersetzt wird. |
Save |
L (1) |
Gibt an, ob der Datensatz gespeichert wird, wenn das Feld aktualisiert wird. |
TimeStamp |
T (8) |
Zeitstempel (nur für VFP-Eintragungen) |
Source |
M (4) |
Quelle für den Inhalt des Datensatzes (VFP-Eintragungen nutzen "Reserved") |
UniqueID |
C (10) |
Eindeutige ID (nur für VFP-Eintragungen) |
User |
M ( 4) |
Verfügbar für benutzerdefinierte Informationen. |
Die Seite Erweitert (Abbildung 24) des IntelliSense-Managers enthält Optionen,
um die Tabelle FoxCode wiederherzustellen und zu bereinigen. Wird die Tabelle
beschädigt oder wird ein Eintrag irrtümlich gelöscht oder geändert, kann die
Tabelle in ihren Originalzustand zurückgesetzt werden. Die Felder TimeStamp,
UniqueID und Save werden geprüft, um festzustellen, ob die Originaldaten geändert
oder ob sie überschrieben wurden. Als Vorgabe verfügen die nativen Einträge
von Visual FoxPro sowohl über einen Zeitstempel als auch über eine eindeutige
ID, das Feld ist aber auf False eingestellt, so dass sie überschrieben werden
können. Benutzerdefinierte Einträge haben auf der anderen Seite keine eindeutige
ID (und benötigen sie auch nicht) oder einen Zeitstempel. Unter der Voraussetzung,
dass das Feld Save auf True steht, werden sie durch Änderungen an der Tabelle
nicht überschrieben oder gelöscht.
Wir beschreiben hier detailliert die unterschiedlichen Typen der Datensätze,
jeweils mit einem Beispiel:
Die Tabelle FoxCode enthält einen einzelnen Datensatz dieses Typs. Er wird für die interne Verwaltung durch die IntelliSense-Engine benötigt. Das Feld Expanded enthält die Versionsnummer der aktuellen Tabelle FoxCode. Das Feld Case definiert den Vorgabewert der Formatierung, die für alle Einträge verwendet werden soll, die eine Formatierung benötigen, aber über keine explizite Einstellung verfügen.
Dieser Typ definiert einen Eintrag für die automatische Vervollständigung, der durch das Leerzeichen ausgelöst wird und der als Vorgabe durch das "Default Script" (das im Feld Data des Datensatzes mit Typ = S und einem leeren Feld Abbrev definiert ist) aufgerufen wird. Alle grundlegenden Befehle von VFP nutzen diese Methodik, um den Inhalt des Felds Expanded an die abgekürzte Form des Befehls anzuhängen.
Die anderen komplexeren Befehle rufen ein generisches Skript für die Behandlung von Befehlen ({cmdhandler}) auf, das im Feld Cmd angegeben ist. Dieses Skript ersetzt den Inhalt des Feldes Abbrev mit dem Inhalt des Feldes Expanded, statt diesen Inhalt anzuhängen. Diese Methodik kann einfach eingesetzt werden, um "benutzerdefinierte Befehle" zu erstellen, denen explizit ein QuickInfo (vom Feld Tip) oder eine Elementeliste (vom Feld Data) zugeordnet wird. Dies geschieht, indem eine Abkürzung definiert und ein Aufruf an das Skript aufgenommen wird.
Beispiel: Das vorgegebene Verhalten der automatischen Vervollständigung des Befehls ON zeigt eine Liste aller Optionen für diesen Befehl an, auch alle Druck- und Menüoptionen. Allerdings ist in der normalen Programmierung der einzige häufig genutzte ON-Befehl ON KEY LABEL. Um eine automatische Komplettierung von "OKL" zu erstellen, das in "ON KEY LABEL" expandiert wird, sobald nach der Eingabe die Leertaste betätigt wird, fügen Sie der Tabelle FoxCode den folgenden Datensatz hinzu:
Type | Abbrev | Expanded | Cmd | Tip | Case | Save |
C |
OKL |
on key label |
{cmdhandler} |
ON KEY [LABEL KeyLabelName] [Command] |
U |
.T. |
Function definiert einen Eintrag zur automatischen Vervollständigung, der
durch eine öffnende runde Klammer ausgelöst wird. Die Inhalte des Feldes Tip
werden für die Anzeige des Smarttipps der QuickInfo benötigt, das die Parametereingabe
mit der im Datensatz definierten vergleicht.
Beispiel: um einen Eintrag für die automatische Vervollständigung einer benutzerdefinierten
Funktion OpenFile() zu erstellen, die durch die Eingabe von "OPF(" ausgelöst
werden soll, fügen Sie der Tabelle FoxCode den folgenden Datensatz hinzu:
Type | Abbrev | Expanded | Tip | Case | Save |
F |
OPF |
OpenFile |
cFileName[, cFileDir[, lReadOnly]] |
M |
.T. |
Damit definieren Sie die Funktion mit drei Parametern. Sobald dem eingegebenen Text ein Komma hinzugefügt wird, wird der hervorgehobene Teil des Tips automatisch synchronisiert.
Ein Eigenschaftselementtyp wird genutzt, um einen Popup-Dialog (oder Wertliste)
anzulegen, der angezeigt wird, wenn einer Eigenschaft eines beliebigen Objekts,
deren Name dem Wert im Feld Abbrev entspricht, ein Wert zugewiesen wird. Das
Feld Cmd zeigt an, ob ein anderweitig in der Tabelle FoxCode definiertes Skript
oder der Inhalt des Feldes Data im aktuellen Datensatz für die Generierung des
Popup verwändet werden soll. Die Tabelle FoxCode wird mit zwei generischen Skripts
ausgeliefert, die mit diesem Datensatztyp eingesetzt werden. Das erste ({color}genannt)
zeigt den Dialog für die Farbauswahl an und ist unterschiedlichen Eigenschaften
zugeordnet, die Farben definieren (z.B. BackColor, BorderColor und FillColor).
Das zweite ({picture} genannt) zeigt den Dialog "Bild öffnen" an, wenn entweder
eine Eigenschaft Icon oder Picture gesetzt werden soll.
Beispiel: Um den Dialog Farben einem benutzerdefinierten Eigenschaftsnamen zuzuordnen,
fügen Sie der Tabelle FoxCode einfach einen Datensatz hinzu und ersetzen Ihren
eigenen Eigenschaftsnamen (beachten Sie, dass der führende "." Im Feld Abbrev
erforderlich ist:
Type | Abbrev | Cmd | Case | Save |
P |
.MyColor |
{color} |
M |
.T. |
Den Einsatz des Skripts werden wir später in diesem Artikel noch detailliert behandeln. Im Moment genügt die Aussage, dass ein Skript auch direkt in das Feld Data eines Datensatzes geschrieben und ausgeführt werden kann, indem in das Feld Cmd leere geschweifte Klammern ("{}") geschrieben werden. Um ein Skript hinzuzufügen, das den Dialog GetFile() anzeigt und den ausgewählten Dateinamen an eine Eigenschaft mit Namen cSourceFile zurückgibt, fügen Sie der Tabelle FoxCode den folgenden Datensatz hinzu:
Type | Abbrev | Cmd | Data | Case | Save |
P |
.cSourceFile |
{} |
LPARAMETER oFoxCode |
M |
.T. |
Immer, wenn einer Eigenschaft mit Namen cSourceFile ein Wert zugewiesen wird, wird der Dialog GetFile() automatisch angezeigt. Bedenken Sie in diesem Zusammenhang, dass IntelliSense nur in der Entwicklungsumgebung funktioniert, zur Laufzeit passiert nichts.
Ein COM-Komponente-Elementtyp wird eingesetzt, um für die IntelliSense-Engine
zu definieren, wo sich die Typbibliothek für eine COM-Komponente oder ein ActiveX-Steuerelement
befindet. Das Feld Data wird eingesetzt, um die GUID und Versionsinformation
zu speichern und das Feld Tip enthält den vollständigen Namen des Steuerelements.
Was auch immer im Feld Abbrev enthalten ist: es wird als Eintrag für die Typenliste
eingesetzt, die mit der Klausel AS zugeordnet wird (DEFINE CLASS...AS..., LOCAL...AS...).
Beispiel: Um Microsofts Steuerelement TreeVies als eine Option in die Typenliste
aufzunehmen, fügen Sie der Tabelle FoxCode den folgenden Datensatz hinzu:
Type | Abbrev | Tip | Data | Save |
O |
TreeView |
Microsoft TreeView Control 6.0 (SP4) |
{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0 |
.T. |
Beachten Sie, dass das Feld Data die korrekte GUID enthalten muss, die aus der Registry ausgelesen werden kann. Die einfachste Möglichkeit, einen Datensatz dieses Typs anzulegen, besteht im Einsatz der Option Typbibliotheken auf der zweiten Seite des IntelliSense-Managers und (wenn erforderlich) dem anschließenden Ändern des vorgegebenen Namens mit dem Editor auf der dritten Seite.
Ein Typangabe-Elementtyp wird eingesetzt, um für IntelliSense einen Eintrag
zu definieren, der nicht über eine Typbibliothek verfügt, aber trotzdem in die
Typenliste aufgenommen wird. Obwohl keine Typbibliothek erforderlich ist, kann
dieser Typ genutzt werden, um benutzerdefinierte Klassen in die Liste aufzunehmen.
Der Inhalt des Feldes Data wird direkt in der Dropdown-Liste angezeigt. Dies
ist auch das einzige Feld, das ausgefüllt werden muss. Allerdings erleichtert
eine Beschreibung im Feld Abbrev die Pflege der Tabelle FoxCode.
Für visuelle Klassen kann die Option Klassen auf der zweiten Seite des IntelliSense-Managers
eingesetzt werden. Für programmatisch definierte Klassen muss der erforderliche
Datensatz allerdings manuell hinzugefügt werden.
Type | Abbrev | Data | Save |
T |
Container |
basecnt OF X:\Projects\Libs\base.vcx" |
.T. |
T |
Header |
basehdr OF X:\Projects\Libs onVisClass.prg |
.T. |
Der Benutzerelementtyp-Datensatz identifiziert die Inhalte als benutzerdefinierter
Autotexteintrag, der, wie ein Befehl-Eintrag, durch die Eingabe eines Leerzeichens
ausgelöst wird. Der Unterschied liegt darin, dass das vorgegebene Verhalten
eines Benutzerelement-Datensatzes darin besteht, den Inhalt des Feldes Abbrev
durch den Inhalt des Feldes Expanded auszutauschen. Es ist nicht erforderlich,
das Skript für die Befehlsbehandlung aufzurufen und dies ist deshalb die bevorzugte
Methode für die Erstellung benutzerdefinierter Shortcuts.
Beispiel: Um den Shortcut "cright" zu expandieren, fügen Sie der Tabelle FoxCode
den folgenden Datensatz hinzu:
Type | Abbrev | Expanded | Case | Save |
U |
cright |
© 2002 Microsoft Corporation |
X |
.T. |
Diese Datensätze können auch Skripts aufrufen. Das Feld Cmd wird eingesetzt, um anzuzeigen, wo sich das Skript befindet. Ein Paar leerer geschweifter Klammern bedeutet, dass sich das Skript im aktuellen Datensatz befindet, während die Angabe eines Namens bedeutet, dass das Skript in einem Skript-Datensatz an anderer Stelle in der Tabelle definiert ist. Die folgende Variation des Shortcut nutzt ein Skript, um exakt das gleiche Ergebnis zu erzielen:
Type | Abbrev | Cmd | Data | Case | Save |
U |
cright |
{} |
LPARAMETER oFoxCode |
X |
.T. |
Skriptelementtypen enthalten Code, der "on the fly" kompiliert und ausgeführt
wird. Diese Datensätze werden für die Definition generischer Skripts genutzt,
die von anderen Datensätzen in der Tabelle FoxCode genutzt werden. Andere Datensätze
stoßen die Ausführung dieser Skripts an, indem sie im Feld Abbrev wie den Inhalt
ihres eigenen Feldes Cmd in geschweiften Klammern den Namen des Skripts enthalten.
Beachten Sie: Mit Ausnahme von T und O können alle Datensatztypen ein Skript
in ihrem eigenen Feld Data enthalten. Um allerdings ein eingebettetes Skript
auszuführen, muss sich im Feld Cmd ein leeres Paar geschweifter Klammern befinden.
Beispiel: Das folgende Skript zeigt den Dialog GetFile() an und liefert an die
aufrufende Quelle zurück, was es vom Dialog erhält. Alles was erforderlich ist,
um der Tabelle FoxCode einen Datensatz hinzuzufügen mit dem Namen des Scripts
im Feld Abbrev und dem erforderlichen Code im Feld Data:
Type | Abbrev | Data | Save |
S |
ChooseFile |
LPARAMETER oFoxCode |
.T. |
Dieser Typ wird verwendet, um Datensätze zu identifizieren, die IntelliSense
nicht automatisch ausführt. Mit Visual FoxPro 7.0 werden nur zwei solche Datensätze
ausgeliefert.
Der erste, dessen Feld Abbrev CustomPEMs enthält, speichert die Einstellungen
für die erweiterten Eigenschaften für die Konfiguration in seinem Feld Data.
Auf diese Eigenschaften kann über die Schaltfläche "Eigenschaften bearbeiten",
auf der Seite Erweitert, des IntelliSense-Managers zugegriffen werden.
Der zweite, dessen Feld Abbrev CustomDefaultScripts enthält, speichert die Namen
aller vom Anwender definierten Skripte, die über die Hooks im vorgegebenen Skript
aufgerufen werden, wenn die erweiterte Eigenschaft lAllowCustomDefScripts auf
.T. steht.
Alle Skripts bestehen im Grunde aus zwei Teilen. Der erste Teil ist die IntelliSense-spezifische Präambel und der zweite der FoxPro-Code, der das gewünschte Ergebnis generiert. Die IntelliSense-spezifische Komponente besteht in der Regel aus drei Elementen.
• |
Eine Parameter-Anweisung. Skripts müssen in der Lage sein, einen einzelnen Parameter zu akzeptieren, der eine Referenz auf das FoxCode-Objekt darstellt. Die Eigenschaften des FoxCode-Objekts sind in der Hilfedatei vollständig dokumentiert und auch auf MSDN verfügbar. |
||||||||||||
• |
Eine Definition der Rückgabe, die das Skript generiert. Dies geschieht durch die Einstellung der Eigenschaft ValueType des Objekts FoxCode. Diese Eigenschaft wird benötigt, um zu entscheiden, wie das Ergebnis der Codeausführung im Skript interpretiert werden muss. Drei Werte sind möglich, die in der folgenden Tabelle dargestellt werden:
|
||||||||||||
• |
Prüfung des Ortes, von dem das Skript aus aufgerufen wird. Dies geschieht durch die Prüfung der Eigenschaft Location des Objekts FoxCode. Selbstverständlich sind nicht alle Aktionen zu jedem Zeitpunkt angebracht und hier haben wir die Möglichkeit, das Skript zu umgehen, es sei denn, wir befinden uns im richtigen Editor. Die für die unterschiedlichen Editortypen generierten Werte finden Sie in der folgenden Tabelle:
|
Beachten Sie bitte: diese Werte erhalten Sie zusammen mit vielen anderen
Informationen über das aktive Fenster durch den Aufruf der Funktion _EdGetEnv()
der FoxTools. Diese Funktionalität ist nicht für IntelliSense spezifisch.
Der Rest des Skripts besteht im Grunde aus normalem FoxPro-Code, der die Eigenschaften
des Objekts FoxCode manipuliert und die erforderlichen Aufgaben ausführt. Die
folgenden Beispiele zeigen den Aufbau und den Einsatz von Skripten.
Vielleicht eine der einfachsten und trotzdem hilfreichsten Anpassungen, die wir mit Hilfe von Skripts an IntelliSense vornehmen können, ist die Ausführung lästiger wiederkehrender Aufgaben wie das Erstellen von Headern für Programme oder Methoden oder auch das Einfügen von Standard-Codeblöcken. Die folgende Abbildung zeigt Ihnen einen Standard-Programmheader, der durch ein Skript erstellt wurde, das durch die Eingabe von "hdr" mit anschließender Leertaste im Editor ausgelöst wurde:
Abbildung 9. Durch ein Skript generierter Programm-Header
Um das Skript zu erstellen, das diesen Textblock generiert, müssen wir der Tabelle FoxCode einen Datensatz hinzufügen und es wie in der folgenden Tabelle beschrieben einrichten:
Feld | Inhalt |
Type |
U |
Abbrev |
hdr |
Cmd |
{ } |
Data |
LPARAMETERS toFoxCode IF toFoxcode.Location <1 RETURN toFoxCode.UserTyped ENDIF toFoxcode.valuetype = "V" LOCAL lcTxt, lcName, lcComment STORE "" TO lcTxt, lcName, lcComment #DEFINE CRLF CHR(13)+CHR(10) lcName = WONTOP() lcVersion = "Visual FoxPro" + VERSION(4) lcComment = INPUTBOX( 'Comment for the header:' ) TEXT TO lcTxt NOSHOW *********************************************************************** * Program....: <<UPPER(lcName)>> * Author.....: Andy Kramek * Date.......: <<DMY(DATE())>> * Notice.....: Copyright (c) <<TRANSFORM( YEAR(DATE()))>> Tightline Computers Inc * Compiler...: <<lcVersion>> * Purpose....: <<lcComment>> *********************************************************************** ~ENDTEXT RETURN TEXTMERGE(lcTxt) |
Obwohl dieses Skript sehr einfach ist, illustriert es doch, wie ein Skript für IntelliSense in zwei Teilen aufgebaut ist. Zunächst sehen wir die für IntelliSense spezifischen Einstellungen, in denen wir einen Parameter einrichten, um eine Referenz auf das Objekt FoxCode erhalten zu können:
LPARAMETERS toFoxCode *** This script returns a Value toFoxcode.valuetype = "V" *** Do nothing unless we are in a program editing window IF toFoxcode.Location # 1 RETURN toFoxCode.UserTyped ENDIF
Anschließend definieren wir, wie die IntelliSense-Engine mit dem Rückgabewert umgeht. Dafür gibt es drei Möglichkeiten:
• |
V (Value): nimmt alles, was von diesem Skript zurückgegeben wurde, als Wert. Als Voreinstellung ersetzt der Wert einfach das Kürzel, dass das Skript ausgelöst hat. |
• |
L (List): nutzt die Inhalte der Collection Items als Quelle für eine Liste. Das Skript muss sicherstellen, dass die Collection korrekt dimensioniert und gefüllt ist. |
• |
T (Quick Tip): zeigt den Inhalt der Eigenschaft ValueTip als QuickInfo an. Das Skript muss den Inhalt dieser Eigenschaft verwalten. |
Zum Schluss prüfen wie die Eigenschaft Location des Objekts FoxCode. Dieser Wert ist als numerischer Wert gespeichert, der von der Funktion EdGetEnv() aus den FoxTools geliefert wird, die ein Array zurückgibt, in dem unterschiedliche Informationen über die aktuelle Session des Editors enthalten sind. Der Fenstertyp (Element 25) wird folgendermaßen angegeben:
0 = Befehls-Fenster 1 = Programm 8 = Menü Snippet 10 = Code Snippet 12 = Gespeicherte Prozedur
Da dieses besondere Skript nur in einer Programmdatei sinnvoll ist, können
wir dessen Gültigbereich auf Location = 1 einschränken. Beachten Sie, dass wir,
wenn das Skript von anderen Fenstern aufgerufen wird, einfach den Inhalt einer
anderen Eigenschaft des Objekts FoxCode zurückgeben. Es handelt sich um die
Eigenschaft UserTyped, die, wie der Name bereits aussagt, alles enthält, was
der Anwender eingegeben hat, um das Skript auszulösen. Das Ergebnis der Eingabe
von "hdr" an anderer Stelle als einem Programm, ergibt also den String "hdr".
Der zweite Teil des Skripts besteht aus normalem Visual FoxPro-Code, der hier
den Text des Headers erstellt. Beachten Sie, dass dieses einfache Skript eine
Mischung alter und neuer Visual FoxPro-Funktionen enthält.
*** Standard FoxPro Code from here on *** Define and initialize local variables LOCAL lcTxt, lcName, lcComment STORE "" TO lcTxt, lcName, lcComment *** Get the window name (if one has been defined) lcName = WONTOP() *** Get the VFP Version Number lcVersion = "Visual FoxPro " + VERSION( 4 ) *** Get the description lcComment = INPUTBOX( 'Comment for the header:' ) TEXT TO lcTxt NOSHOW *********************************************************************** * Program....: <<UPPER(lcName)>> * Author.....: Andy Kramek * Date.......: <<DMY(DATE())>> * Notice.....: Copyright (c) <<TRANSFORM( YEAR(DATE()))>> Tightline Computers Inc * Compiler...: <<lcVersion>> * Purpose....: <<lcComment>> *********************************************************************** ~ ENDTEXT RETURN TEXTMERGE(lcTxt)
Nach der Definition und Initialisierung der erforderlichen lokalen Variablen
beginnen wir, ihnen Werte zuzuweisen, indem wir mit der alten Funktion WONTOP()
den Namen des aktuellen Fensters ermitteln. Anschließend beziehen wir die Versionsnummer
von VFP über die Option 4 der Funktion VERSION() und nutzen wir die brandneue
Funktion INPUTBOX(), um vom Entwickler einen Kommentar für den Header zu erhalten.
Der Textblock, den wir einfügen wollen, wird mit dem neu erweiterten Konstrukt
TEXT...ENDTEXT als String erstellt, was es uns jetzt ermöglicht, den Text direkt
in eine Variable zu schreiben, als String erstellt. Beachten Sie den Einsatz
der Tilde in der letzten Zeile des Header, wodurch wir sicherstellen, dass der
Cursor an der richtigen Stelle positioniert wird. Am Ende nutzt die Anweisung
RETURN die andere neue Funktion TEXTMERGE(), um die Variable zu prüfen und den
sich ergebenden Text als Austauschtext für die definierte Abkürzung zu verwenden.
Eines der offensichtlichsten Features von IntelliSense sind die unterschiedlichen
Einsatzmöglichkeiten, die für Listen offen stehen. Viele der nativen Befehle
und Funktionen von Visual FoxPro implementieren Listen und Sie können auf einfache
Weise Ihre eigenen Listen erstellen. Die einfachste Version ist eine statische
Liste, in der Sie die Einträge definieren, die Sie direkt im Datenfeld eines
benutzerdefinierten Datensatzes anzeigen und anschließend das native Skript
CmdHandler aufrufen, um die Liste zu generieren.
Das folgende Beispiel zeigt, wie wir eine solche einfache Liste definieren können,
die es uns ermöglicht, benutzerdefinierte Shortcuts für die nativen Befehle
von VFP zu erstellen, die wir häufig einsetzen.
Abbildung 10. Eine einfache statische Liste
Hier ein benutzerdefinierter Shortcut (CSPB), der als Liste zwei vordefinierte Optionen anzeigt, die für die Vervollständigung des Befehls CusorSetProp eingesetzt werden kann (in den anderen Modi nutzen wir diesen Befehl normalerweise nicht). Um diesen und ähnliche Shortcuts zu erstellen, müssen wir der Tabelle FoxCode einen Datensatz mit den folgenden Charakteristika hinzufügen:
Feld | Inhalt |
Type |
U |
Abbrev |
CSPB |
Expanded |
CURSORSETPROP( |
Cmd |
{CmdHandler} |
Data |
"Buffering", 5, ALIAS() ) && Table Buffered
|
Das vorgegebene Verhalten des Skripts CmdHandler besteht darin, die Einträge des Feldes Data zu lesen und diese als Liste anzuzeigen. Der Text im Feld abbrev wird durch den Inhalt des Feldes expanded ersetzt.
Allerdings ist dies nicht die einzige Möglichkeit, eine statische Liste zu erstellen. Wie wir bereits bei der Beschreibung des Headerskripts bemerkt haben, bietet das Objekt FoxCode die Collection Items an, die für die Generierung von Listen genutzt wird, wenn die Eigenschaft ValueType auf "L" steht. Wir können daher eine Liste generieren, indem wir ein Skript erstellen, das direkt in die Collection Items schreibt. Dieses Vorgehen hat einen entscheidenden Vorteil gegenüber der Lösung mit CmdHandler, da die Collection Items bereits über zwei Spalten verfügt. Die Inhalte der ersten Spalte werden direkt in der Liste angezeigt und die Inhalte der zweiten werden benötigt, um die Wertliste zu füllen, die auch in den meisten nativen Listen auftaucht.
Das nächste Beispiel füllt die Collection Items mit einer Liste dreier möglicher Referenzen, die eingesetzt werden, um eine lokale Variable zu erstellen, die ein Objekt referenziert. Die folgende Abbildung zeigt die Liste, die durch die Eingabe von "obj", gefolgt von einem Leerzeichen, ausgelöst wird.
Abbildung 11. Eine statische Liste, die unter Einsatz der Collection FoxCode.Items erstellt wurde
In diesem Beispiel nutzen wir wieder das Feld expanded, um den Text zu ersetzen, der das Skript und die Liste für die Vervollständigung der Anweisung ausgelöst hat, Der erforderliche Datensatz in FoxCode sieht folgendermaßen aus:
Field | Content |
Type |
U |
Abbrev |
Obj |
Expanded |
loRef = |
Cmd |
{} |
Data |
LPARAMETER toFoxCode WITH toFoxCode .ValueType = "L" DIMENSION .Items[3,2] .Items[1,1] = "This" .Items[1,2] = "Current Object" .Items[2,1] = "ThisForm" .Items[2,2] = "Current Form" .Items[3,1] = "This.Parent" .Items[3,2] = "Immediate Parent Container" RETURN ALLTRIM( .Expanded ) ENDWITH |
Statische Listen sind manchmal hilfreich. Die Funktionalität, die in IntelliSense
für sich allein gesehen die größte Hilfe bei der Steigerung der Produktivität
bringt, ist aber wahrscheinlich die Möglichkeit, dynamisch Listen zu generieren.
Dieser Prozess ist etwas komplexer als die Beispiele, die wir bislang gesehen
haben, aber Sie werden feststellen, dass die Ergebnisse die aufgewendete Zeit
rechtfertigen.
Das folgende Beispiel nutzt ein generisches Skript, um eine Liste von Dateien
eines bestimmten Typs im aktuellen Verzeichnis und in unmittelbaren Unterverzeichnissen
zu erstellen. Der Gedanke dabei ist, dass dieses Skript von anderen Shortcuts
aufgerufen wird, die den speziellen Dateityp definieren, der zurzeit benötigt
wird. Weshalb sich mühen, wenn wir bereits die MRU-Listen haben? Nun, damit
eine Datei in einer MRU-Liste angezeigt wird, müssen Sie diese Datei mindestens
einmal benutzt haben. Außerdem muss diese Nutzung innerhalb der letzten n Dateien
geschehen sein, wobei n die Zahl repräsentiert, die Sie als Limit der MRU-Listen
angegeben haben. Abbildung 28 zeigt das Ergebnis der Eingabe des Shortcut "mop"
(für "Modify Program") in der Befehlszeile.
Abbildung 12. Eine dynamische Liste von .PRG-Dateien im aktuellen Verzeichnis und den unmittelbaren Unterverzeichnissen
Als erstes müssen wir einen Shortcut-Eintrag in der Tabelle FoxCode anlegen. Dies ist ein einfacher benutzerdefinierter Datensatz, wie wir ihn ja bereits einmal angelegt haben. Der einzige Unterschied besteht hier darin, dass wir kein im Feld Data stehendes Skript aufrufen, sondern das generische Skript ShoFile im Feld Cmd aufrufen. Der Datensatz sieht folgendermaßen aus:
Feld | Inhalt |
Type |
U |
Abbrev |
Mop |
Expanded |
MODIFY COMMAND |
Cmd |
{shofile} |
Das aktuelle Skript ist etwas komplexer als diejenigen, die wir bereits gesehen haben, da wir Manipulationen in der Art vornehmen müssen, in der das Objekt FoxCode behandelt wird. Dafür müssen wir von der Klasse FoxCodeScript (definiert in FoxCode.prg) eine Unterklasse ableiten. Hier der erste Teil des Skripts:
LPARAMETER toFoxcode IF FILE(_CODESENSE) LOCAL luRetVal, loFoxCodeLoader *** Ensure we have the root class definition SET PROCEDURE TO (_CODESENSE) ADDITIVE *** Create an instance of our custom sub class loFoxCodeLoader = CreateObject("FoxCodeLoader") *** Call it's Start() method and pass the FoxCode object luRetVal = loFoxCodeLoader.Start( toFoxCode ) *** Clean up nicely here loFoxCodeLoader = NULL IF ATC(_CODESENSE,SET("PROC"))#0 RELEASE PROCEDURE (_CODESENSE) ENDIF *** Return whatever we got back RETURN luRetVal ENDIF
Beachten Sie, dass dieser Code davon ausgeht, dass die neue Systemvariable
_CodeSense auf ein Programm verweist, das die Klassendefinition für das Objekt
FoxCodeScript enthält. Der zweite Teil des Skripts (unten) definiert die benutzerdefinierte
Unterklasse, die hier instanziiert ist und die Referenz auf das Objekt FoxCode
erhält. Der Rest dieses Teils des Skripts stellt lediglich sicher, dass das
Objekt sauber freigegeben wird und keine hängenden Referenzen zurückbleiben.
Das ist die Standardvoraussetzung für die Erstellung eines benutzerdefinierten
Skripts.
Im zweiten Teil des Skripts definieren wir die abgeleitete Klasse, die die erforderlichen
Aufgaben ausführt. Die Klasse FoxCodeScript definiert eine benutzerdefinierte
Eigenschaft mit Namen oFoxCode, in der die übergebene Referenz auf das Objekt
FoxCode festgehalten wird, sowie eine Methoden-Template namens Main(), das von
der Methode Start() aufgerufen wird, wenn das Objekt instanziiert ist. Hier
platzieren wir unseren speziellen Code:
DEFINE CLASS FoxCodeLoader as FoxCodeScript PROCEDURE Main() LOCAL lcMenu, lcKey lcMenu = THIS.oFoxcode.MenuItem IF EMPTY( lcMenu ) *** Nothing selected, so display list lcKey = UPPER( THIS.oFoxcode.UserTyped ) *** What sort of files do we want DO CASE CASE INLIST( lcKey, "MOP","DOP" ) lcFiles = '*.prg' CASE INLIST( lcKey, "MOF", "DOF" ) lcFiles = '*.scx' CASE INLIST( lcKey, "MOR", "DOR" ) lcFiles = '*.frx' OTHERWISE lcFiles = "" ENDCASE *** Populate the Items Array for display This.GetItemList( lcFiles ) *** Return the Expanded item RETURN This.AdjustCase()
Es ist wichtig, sich darüber im Klaren zu sein, dass wir dieses Skript zweimal aufrufen. Der erste Aufruf erfolgt, wenn die IntelliSense-Engine auf Ihren definierten Shortcut reagiert (in diesen Falle "Mop"). Das Skript wird explizit vom Datensatz in der Tabelle FoxCode aufgerufen und übergibt wie üblich eine Referenz auf das Objekt FoxCode. Natürlich haben wir zu diesem Zeitpunkt nur "mop" eingegeben, so dass die Eigenschaft MenuItem des Objekts FoxCode leer ist und der Code am Anfang der Methode Main() wird ausgeführt.
Wie Sie sehen können, wird die Eigenschaft UserTyped des Objekts FoxCode überprüft, um zu entscheiden, welcher Shortcut eingegeben wurde, der entsprechende Dateityp wird eingerichtet und die benutzerdefinierte Methode GetItemList() aufgerufen. Diese Methode ist für die Ausführung eines ADIR() verantwortlich, um die Dateinamen zu erhalten und kopiert die Liste in die Collection Items des Objekts FoxCode. Anschließend wird die Eigenschaft ValueType auf "L" gesetzt (um die Liste anzuzeigen). Danach wird die Eigenschaft ItemScript eingestellt, um auf das Skript ShoFile zu verweisen.
Die Eigenschaft ItemScript wird von der IntelliSense-Engine benötigt, um festzustellen, welche Aktion ausgeführt werden soll, wenn ein Anwender einen Eintrag aus der Liste auswählt. Der Rückgabewert dieses Skripts ist in der Regel ein Aufruf eines Datensatzes vom Typ User, um den Shortcut durch den Inhalt des Feldes Expanded (in diesem Fall "Modify Command") zu ersetzen. Zusätzlich wird die durch GetItemList() generierte Liste angezeigt. Obwohl die Eigenschaft ItemScript jetzt auf das Skript ShoFile verweist, wenn in der Liste eine Auswahl getroffen wird, hat die Eigenschaft MenuItem des Objekts FoxCode einen Wert und der zweite Teil der Methode Main() wird ausgeführt:
ELSE *** Return the Selected item This.oFoxCode.ValueType = "V" RETURN lcMenu ENDIF ENDPROC
Hier geschieht nichts anderes, als dass die Eigenschaft ValueType auf "V"
zurückgesetzt und die Auswahl aus der Liste (jetzt in der Variablen lcMenu)
als Rückgabewert zurückgegeben wird, um die Operation zu vervollständigen.
Der Code in der Methode GetItemList() besteht mit Ausnahme des Teils, der das
Objekt FoxCode für den zweiten Durchgang einstellt, aus Standard-Code von FoxPro,
um eine Liste der Dateien im aktuellen Verzeichnis sowie den direkten Unterverzeichnissen
zu erhalten:
PROCEDURE GetItemList( tcKey ) LOCAL ARRAY laFiles[1,2], laDirs[1], laJunk[1] LOCAL lcRootDir, lnDirs, lnDCnt, lnFiles, lcNewDir, lnFound, lcCurDir, lnFCnt, lcFile *** Save Root Directory lcRootDir = FULLPATH( CURDIR()) *** Get sub-directories lnDirs = ADIR( laDirs, '*.' , 'D' ) lnFiles = 0 *** And Process them FOR lnDCnt = 1 TO lnDirs *** Return to root and re-set CD ( lcRootDir ) DIMENSION laJunk[1] lcNewDir = UPPER( ALLTRIM( laDirs[ lnDCnt, 1 ] )) IF lcNewDir = ".." *** Don't go up the tree LOOP ENDIF CD ( lcNewDir ) lnFound = ADIR( laJunk, lcFiles ) lcCurDir = FULLPATH( CURDIR()) *** If we didn't get any just go on IF lnFound < 1 LOOP ENDIF *** Now process the files FOR lnFCnt = 1 TO lnFound lcFile = lcCurDir + laJunk[ lnFCnt, 1 ] lnFiles = ALEN( laFiles, 1 ) + 1 IF NOT EMPTY( laFiles[1] ) DIMENSION laFiles[ lnFiles, 2 ] ELSE lnFiles = 1 ENDIF laFiles[ lnFiles, 1 ] = lcFile NEXT NEXT *** Change back to original directory CD (lcRootDir) *** If we got something, display the list IF lnFiles > 0
An diesem Punkt verfügen wir über eine Liste aller Dateien des erforderlichen Typs im Array laJunk. Jetzt müssen wir das Objekt FoxCode einrichten, damit es die Liste anzeigt und dieses Skript neu aufruft:
THIS.oFoxcode.ValueType = "L" THIS.oFoxcode.ItemScript = "ShoFile" *** Copy items to temporary array DIMENSION THIS.oFoxcode.Items[lnFiles ,2] ACOPY(laFiles,THIS.oFoxcode.Items) ENDIF ENDPROC ENDDEFINE
Das ist gar nicht so komplex wie es aussieht. Hier der gesamte Datensatz ShoFile:
Feld | Inhalt |
Type |
S |
Abbrev |
Shofile |
Cmd |
{} |
Data |
LPARAMETER toFoxcode IF FILE(_CODESENSE) LOCAL luRetVal, loFoxCodeLoader *** Ensure we have the root class definition SET PROCEDURE TO (_CODESENSE) ADDITIVE *** Create an instance of our custom sub class loFoxCodeLoader = CreateObject("FoxCodeLoader") *** Call it's Start() method and pass the FoxCode object luRetVal = loFoxCodeLoader.Start( toFoxCode ) *** Clean up nicely here loFoxCodeLoader = NULL IF ATC(_CODESENSE,SET("PROC"))#0 RELEASE PROCEDURE (_CODESENSE) ENDIF *** Return whatever we got back RETURN luRetVal ENDIF DEFINE CLASS FoxCodeLoader as FoxCodeScript PROCEDURE Main() LOCAL lcMenu, lcKey lcMenu = THIS.oFoxcode.MenuItem IF EMPTY( lcMenu ) *** Nothing selected, so display list lcKey = UPPER( THIS.oFoxcode.UserTyped ) *** What sort of files do we want DO CASE CASE INLIST( lcKey, "MOP","DOP" ) lcFiles = '*.prg' CASE INLIST( lcKey, "MOF", "DOF" ) lcFiles = '*.scx' CASE INLIST( lcKey, "MOR", "DOR" ) lcFiles = '*.frx' OTHERWISE lcFiles = "" ENDCASE *** Populate the Items Array for display This.GetItemList( lcFiles ) *** Return the Expanded item RETURN This.AdjustCase() ELSE *** Return the Selected item This.oFoxCode.ValueType = "V" RETURN lcMenu ENDIF ENDPROC PROCEDURE GetItemList( tcKey ) LOCAL ARRAY laFiles[1,2], laDirs[1], laJunk[1] LOCAL lcRootDir, lnDirs, lnDCnt, lnFiles, lcNewDir LOCAL lnFound, lcCurDir, lnFCnt, lcFile *** Save Root Directory lcRootDir = FULLPATH( CURDIR()) *** Get sub-directories lnDirs = ADIR( laDirs, '*.' , 'D' ) lnFiles = 0 *** And Process them FOR lnDCnt = 1 TO lnDirs *** Return to root and re-set CD ( lcRootDir ) DIMENSION laJunk[1] lcNewDir = UPPER( ALLTRIM( laDirs[ lnDCnt, 1 ] )) IF lcNewDir = ".." *** Don't go up the tree LOOP ENDIF CD ( lcNewDir ) lnFound = ADIR( laJunk, lcFiles ) lcCurDir = FULLPATH( CURDIR()) *** If we didn't get any just go on IF lnFound < 1 LOOP ENDIF *** Now process the files FOR lnFCnt = 1 TO lnFound lcFile = lcCurDir + laJunk[ lnFCnt, 1 ] lnFiles = ALEN( laFiles, 1 ) + 1 IF NOT EMPTY( laFiles[1] ) DIMENSION laFiles[ lnFiles, 2 ] ELSE lnFiles = 1 ENDIF laFiles[ lnFiles, 1 ] = lcFile NEXT NEXT *** Change back to original directory CD (lcRootDir) *** If we got something, display the list IF lnFiles > 0 THIS.oFoxcode.ValueType = "L" THIS.oFoxcode.ItemScript = "ShoFile" *** Copy items to temporary array DIMENSION THIS.oFoxcode.Items[lnFiles ,2] ACOPY(laFiles,THIS.oFoxcode.Items) ENDIF ENDPROC ENDDEFINE |
Das Skript, so wie es hier steht, beinhaltet die Behandlung von Programmen
(mop & dop), Formularen (mof und dof) und Berichten (mor und dor). Selbstverständlich
ist es eine einfache Aufgabe, diesem Skript zusätzliche Typen hinzuzufügen.
Dies überlassen wir dem Leser als Übung.