NEU in XProfan 11

XProfan ohne Grenzen

Statisches Linken externer Funktionen / CDECL-Aufrufe

Array-Zuweisungen und Matrix-Befehle

Dynamische Arrays

Übernahme von Strukturen in Strukturen und Klassen

Prozess-IDs und Exit-Codes

Neue Bitmapfunktionen und Grafikformate

Metafiles

TrayIcons

Kollisionen und Überschneidungen

Zusätzliche Formatierungsmöglichkeiten bei Format$

TreeView

ImageList

Browser: HTMLWin

Subclassing mit XProfan

Erweiterte Befehle und Funktionen

Sonstige Erweiterungen

Anhang 1: Behobene Bugs

Anhang 2: Mögliche Kompatibilitätsprobleme


XProfan ohne Grenzen

Auch wenn man die folgende Änderung auf dem ersten Blick nicht "sieht", so ist sie doch die bislang aufwändigste Erweiterung der neuen Version:

  • Die Anzahl der Programmzeilen ist nun unbegrenzt.*
  • Die Anzahl der Variablen pro Typ und die maximale Anzahl der Elemente von Arrays eines Typs ist nun unbegrenzt.*
  • Die PROC-ENDPROC-Verschachtelung, und damit die Rekursionstiefe ist unbegrenzt.**
  • Die WHILE-WEND-Verschachtelung ist unbegrenzt.*
  • Die Anzahl der XPGL-Daten im Speicher ist unbegrenzt. *
  • Die maximale Anzahl der Spalten bei SQL-Abfragen (bislang 1024) ist unbegrenzt. *

* Aus technischen Gründen gibt es noch eine Grenze, die bei etwas über 2 Milliarden liegt (maximaler Wert eines vorzeichenbehafteten Longint), aber dies dürfte in der Praxis wohl in keinem Fall erreichbar sein, bevor der Rechner wegen mangelnden Speichers aufgibt.

** In der Praxis begrenzt hier die Stackgröße die Rekursionstiefe bei einigen Tausend. Es ist daher empfehlenswert, eine Sicherheitsabfrage einzubauen.

Diese "Grenzenlosigkeit" wurde durch eine dynamischere Speicherverwaltung erzielt (offene Arrays in Delphi) und hat zur Folge, dass die meisten Programme in Zukunft mit deutlich weniger Hauptspeicher auskommen werden.


Statisches Linken externer Funktionen

Was ist der Unterschied zwischen dem bisherigen dynamischen Linken und dem statischen Linken?

Beim dynamischen Linken wird erst beim Aufruf die DLL gegebenenfalls geladen und dabei überprüft, ob sie überhaupt vorhanden ist. Auch wenn eine externe Funktion mit DEF am Programmbeginn definiert wird, erfolgt das Laden und Überprüfen erst beim Aufruf.

Beim statischen Linken erfolgt das Laden und Überprüfen schon beim Importieren der DLL-Funktionen, so dass beim Aufruf diese Schritte entfallen und der Aufruf dadurch erheblich beschleunigt wird.

a& = importFunc(hDLL&, Func$, Alias$)

hDLL& = Handle der DLL (wird mit UseDLL ermittelt)

Func$ = Name der Funktion in der DLL (Auf Groß/Kleinschreibung achten!)

Alias$ = Name, unter dem die Funktion in XProfan aufgerufen wird. Wenn es ein Leerstring ist, dann wird Func$ für den Aufruf genommen.

Ergebnis a& = Adresse der importierten Funktion

Mit dieser Funktion wird eine externe Funktion importiert. Die zugehörige Datei (in der Regel eine DLL) muß zu diesem Zeitpunkt verfügbar sein, ansonsten erfolgt eine Fehlermeldung.

Die so gelinkte Funktion kann im Programm nun unter ihrem zugewiesenen Namen aufgerufen werden.

h& = importDLL(DLL$, Name$)

DLL$ = Name (ggf. mit Pfad) der DLL

Name$ = Namenszusatz, der vor jeden Funktionsnamen gesetzt wird. Das darf auch ein Leerstring sein.

Ergebnis n& = Handle der geladenen DLL

Mit dieser Funktion werden alle Funktionen einer DLL importiert. Die zugehörige DLL muß zu diesem Zeitpunkt verfügbar sein, ansonsten erfolgt eine Fehlermeldung. Die DLL wird durch diese Funktion geladen und sollte beim Programmende wieder mit FreeDLL entladen werden.

Die so gelinkten Funktionen können im Programm nun unter ihrem um den Namenszusatz erweiterteten Namen aufgerufen werden.

Es gibt eine Sonderfunktion, wenn Name$ ein Stern ("*") ist: Die Funktionsnamen der DLL werden der Listboxliste hinzugefügt. Ein Linken der Funktionen erfolgt in diesem Falle nicht. Auf diese Weise lassen sich die aufrufbaren Funktionen einer DLL ermitteln. Das Ergebnis ist in diesem Fall die Anzahl der Funktonen in der DLL.

CDECL-Aufrufe

Da es prinzipiell möglich ist, beim Aufruf externer Funktionen bezüglich der Parameterreihenfolge auf dem Stack oder der Frage, ob Aufrufer oder Funktion anschließend den Stack bereinigen, unterschiedliche Wege zu berschreiten, gibt es die Aufruf-Konventionen (calling conventions). Mit der Aufruf-Modus wird beim Aufruf einer externen festgelegt, welche Konvention genutzt wird. Bislang ünterstützte XProfan beim Aufruf von externen Funktion in DLLs nur den Windows-Standard "STDCALL". Dieser ist auch weiterhin voreingestellt. Es gibt aber auch DLLs, die den Aufruf-Modus "CDECL" verlangen. Diese konnten bislang von XProfan aus nicht genutzt werden. Mit einer neuen Set-Funktion kann der Aufruf-Modus in XProfan eingestellt werden:

Set("CallConv", Modus$)

Modus$: Aufrufkonvention - "STDCALL" oder "CDECL"

Modus$ = Get("CallConv")

Ergebnis: Aktueller Aufruf-Modus

Es werden nur die Modi "STDCALL" und "CDECL" unterstützt. Groß-/Kleinschreibung ist ohne Bedeutung. Der eingestellte Modus gilt für alle Aufrufe externer Funktionen, bis er wieder umgestellt wird.

Hinweis: Wichtig ist der Aufruf-Modus nur direkt beim Ausführen der Funktion, also beim Aufruf über ihren Namen, bei Call() oder External(). Beim Definieren einer externen Funktion mit DEF oder beim Linken mit ImportDLL() oder ImportFunc() spielt der Aufruf-Modus keine Rolle!

Auf Callback-Funktionen oder den internen oGL-Funktionen über oGL() hat der Aufruf-Modus keine Wirkung. Hier bleibt es immer beim gewohnten "STDCALL".


Array-Zuweisungen und Matrix-Befehle

Array-Zuweisungen

A&[] = B&[]

Ein Array kann einem anderen zugewiesen werden, wenn es den gleichen Typ und die gleichen Dimensionen hat, bzw. beide dynamisch sind.

Diese Möglichkeit gilt für alle Arraytypen mit Ausnahme der Bereichs-, Struktur- oder Objektarrays.

A$[] = Explode(text$, d$)

Der Inhalt von text$ wird in Einzelstrings zerlegt, die durch den Delimiter d$ getrennt sind. A$[] enthält die Einzelstrings.

Explode liefert ein dynamisches Stringarray zurück. A$[] muß daher ein dynamisches Stringarray sein. Beispiel:

   Declare A$[]

   CLS

   var test$ = "Das ist ein Test"

   A$[] = Explode(test$, " ")

   WhileLoop 0, SizeOf(A$[]) - 1

     Print A$[&loop]

   EndWhile

   WaitInput

   End

Parameters A&[]

Einer Prozedur kann über Parameters ein Array übergeben werden.

Diese Möglichkeit gilt für alle Arraytypen mit Ausnahme der Bereichs-, Struktur- oder Objektarrays.

PType() um Arrays ergänzt

Ist der Parameter ein dynamisches Array, ist das Ergebnis der Parametertyp gefolt von leeren eckigen Klammern, z.B. "$[]". Ist der Parameter ein Array mit definierter Größe, stehen in den eckigen Klammern die Dimensionsgrößen, etwa "&[8,8,2]".

Return A&[]

Eine Prozedur kann ein Array zurückgeben. Das Ergebnis muß einem Array gleichen Typs zugewiesen werden.

Diese Möglichkeit gilt für alle Arraytypen mit Ausnahme der Bereichs-, Struktur- oder Objektarrays.

A&[] = <Ausdruck>

&Index

Allen Elementen das Arrays wird <Ausdruck> zugewiesen. Diese Möglichkeit gilt für alle Arraytypen mit Ausnahme der Bereichs-, Struktur- oder Objektarrays.

Im Ausdruck darf die Systemvariable &Index vorkommen, die den jeweiligen Indexwert hat. Bei mehrdimensionalen Arrays wird dieser Indexwert über alle Elemente hochgezählt, so als handele es sich um ein eindimmsionales Array. Beispiel:

Declare Feld$[20]

Feld$[] = "Feld " + Str$(&Index)

CLS

WhileLoop 0, 20

   Print Feld$[&Loop]

EndWhile

WaitInput

End

Diese Array-Zuweisung ist deutlich schneller als eine entsprechende Schleife. Allerdings ist sie trotzdem immer noch deutlich langsamer, als die folgenden Matrix-Operationen. Bei der Array-Zuweisung wird der Ausdruck jedesmal neu errechnet (wegen des möglichen Vorkommens von &Index), was bei den Matrix-Operationen nicht passiert. Deshalb darf dort &Index auch nicht verwandt werden. &Index darf ausschließlich bei Array-Zuweisungen verwandt werden.

Matrix-Operationen

Die Matrix-Operationen gelten für alle fixen Array-Typen mit Ausnahme der Bereichs-, Struktur- oder Objektarrays.

Hinweis: Für dynamische Arrays kann MAT nicht eingesetzt werden.

MAT A&[] = <Ausdruck>

Allen Elementen des Arrays A& wird der Wert <Ausdruck> zugewiesen.

MAT A&[] + <Ausdruck>

Zu allen Elementen des Arrays A& wird <Ausdruck> addiert. Im Falle von Stringarrays wird der String <Ausdruck> angehängt.

MAT A&[] - <Ausdruck>

Von allen Elementen des Arrays A& wird <Ausdruck> subtrahiert. Für Stringarrays ist das nicht möglich.

MAT A&[] / <Ausdruck>

Alle Elemente des Arrays A& werden durch <Ausdruck> dividiert. Für Stringarrays ist das nicht möglich. <Ausdruck> darf nicht den Wert 0 haben.

MAT A&[] * <Ausdruck>

Alle Elemente des Arrays A& werden mit <Ausdruck> multipliziert. Für Stringarrays ist das nicht möglich.

Bei den folgenden Operationen müssen Quell- und Ziel-Array den selben Typ haben. Ist das Zielarray größer, bleiben die restlichen Elemente unverändert, ist es kleiner als das Quellarray, erfolgt eine Warnmeldung.

MAT A&[] = B&[]

Allen Elementen des Arrays A& wird der entsprechende Wert des Arrays B& zugewiesen.

MAT A&[] + B&[]

Zu allen Elementen des Arrays A& wird der entsprechende Wert des Arrays B& addiert. Im Falle von Stringarrays wird er angehängt.

MAT A&[] - B&[]

Von allen Elementen des Arrays A& wird der entsprechende Wert des Arrays B& subtrahiert. Für Stringarrays ist das nicht möglich.

MAT A&[] / B&[]

Alle Elemente des Arrays A& werden durch den entsprechenden Wert des Arrays B& dividiert. Für Stringarrays ist das nicht möglich. Im Array B& darf kein Wert 0 vorkommen!

MAT A&[] * B&[]

Alle Elemente des Arrays A& werden mit dem entsprechenden Wert des Arrays B& multipliziert. Für Stringarrays ist das nicht möglich.


Dynamische Arrays

Bislang mußte bei der Deklaration eines Arrays die Anzahl der Elemente vorgeben werden, z.B.:

DECLARE Text$[1000]

Bei eindimensionale Arrays kann man nun auf die Angabe einer festen Größe verzichten:

DECLARE Text$[]

Ein so erzeugtes Array hat zunächst eine Größe von 0. Sobald jedoch ein Element zugewiesen wird, wird das Array auf die entsprechende Größe erweitert. Immer, wenn ein Element zugewiesen wird, dass die Größe des bisherigen Arrays überschreitet, wird es auf diese Weise erweitert:

DECLARE Text$[]

Text$[345] = "345"

Text$[1200] = "1200"

Um das Array wieder auf 0 zu verkleinern, ist der Clear-Befehl zu verwenden:

CLEAR Text$[]

Anschließend hat das Array wieder die Größe 0. Die aktuelle Größe des Arrays läßt sich wie gehabt mit der Funktion @SizeOf() ermitteln.

Um die Größe des Arrays beliebig einzustellen, ist der neue Befehl SetSize zu verwenden:

SetSize Text$[], 1000

Auf diese Weise kann ein dynamisches Arrtay beliebig vergrößert oder verkleinert werden.

Einschränkungen gegenüber "normalen" Arrays:

- Es ist nur eine Dimension möglich

- der MAT-Befehl funktioniert nicht mit dynamischen Arrays

- Arrays von Bereichsvariablen (auch Strukturen, Objekte) können nicht dynamisch sein


Übernahme von Strukturen in Strukturen und Klassen

Bislang konnten Strukturen und Klassen Eigenschaften bestehender Strukturen nur über die Vererbung übernehmen. Die zu beerbende Struktur (oder Klasse) mußte an erster Stelle in der Beschreibung der Struktur oder Klasse mit STRUCT oder CLASS stehen. Das bleibt auch so bestehen.

Eine Struktur kann nun mehrere Strukturen beerben und diese müssen nicht am Anfang der Strukturbeschreibung stehen. Es wird ein Fehler ausgegeben wenn ein Element in mehrere der zu beerbenden Strukturen vorkommt oder ein Element bereits in der Struktur vorhanden ist.

Neu in XProfan 2008: Es können zusätzlich Strukturen unter einem frei zu wählenden Namen eingefügt werden. Der Name der Struktur in der Struktur wird von einem Ausrufezeichen getrennt vor dem Strukturnamen angegeben. Ein Beispiel:

   STRUCT Punkt = X%, Y%

   STRUCT Linie = von!Punkt, bis!Punkt

Die Struktur Punkt wird hier zwei mal mit verschiedenen Namen hinzugefügt. Beim Zugriff auf die Elemente ist dieser Name mit zu verwenden:

   LINE von!x%, von!y% - bis!x%, bis!y%

Ein komplettes Beispiel:

   struct punkt = x&, y&

   struct linie = von!punkt, bis!punkt

   declare ber1#

   dim ber1#, linie

   ber1#.von!x& = 300

   ber1#.von!y& = 50

   ber1#.bis!x& = 400

   ber1#.bis!y& = 60

   cls

   line ber1#.von!x&, ber1#.von!y& - ber1#.bis!x&, ber1#.bis!y&

   waitinput

   end

Auch Klassen können auf diese Weise mehrere Strukturen hinzugefügt werden. Es können sogar die Eigenschaften weiterer Klassen so hinzugefügt werden. WICHTIG: Die Vererbung beschränkt sich wie bisher auf eine Klasse oder Struktur (ohne Namen und Ausrufezeichen). Methoden einer anderen Klasse werden nur bei Vererbung übernommen! Ein Beispiel:

   class punkt = x&, y&, zeichne@

   proc punkt.zeichne

     setpixel .x&, .y&, 0

   endproc

   class linie = punkt, bis!punkt, zeichne@

   proc linie.zeichne

     line .x&, .y& - .bis!x&, .bis!y&

   endproc

   declare ber1#

   dim ber1#, linie

   ber1#.x& = 300

   ber1#.y& = 50

   ber1#.bis!x& = 400

   ber1#.bis!y& = 60

   cls

   ber1#.zeichne()

   waitinput

   end


Prozess-IDs und Exit-Codes

END [exitcode%]

Hinter dem END kann ein Exitcode angegeben werden. Dieser kann z.B. vom aufrufenden Programm abgefragt werden. Wird der Parameter nicht benutzt, ist der Exitcode eines fehlerfrei beendeten XProfan-Programmes wie bisher 0.

pid% = WinExec(programm&, mode%)

Diese Funktion liefert jetzt die Prozeß-ID des gestarteten Programmes zurück, wie es im Taskmanager angezeigt wird. Im Fehlerfall ist es wie bisher der Wert 0.

Da ich bislang keine Prozess-ID unter 32 beobachten konnte und und laut bisheriger Hilfe ein Wert unter 32 einen Fehler beim Aufruf bedeutet, während 32 und größer OK sind, sollte es durch diese Änderung keinerlei Kompatibilitätsprobleme geben.

getExitcode(pid%)

Der Exitcode des Prozesses pid% wird ermittelt. Ist das Ergebinis 259 (= STILL_ALIVE) ist das Programm noch nicht beendet.

&pid

Prozess-ID des laufenden XProfan-Programmes

%ExitCode

Der Exitcode des zuletzt mit WinExecWait gestarten Programmes


Neue Bitmapfunktionen und Grafikformate

set("PicBackCol", farbe%)

farbe% = get("PicBackCol")

Das Bitmapformat von Windows kennt von Hause aus keine Transparenz, daher legt XProfan bei create("hPic",..), create("hSizedPic",...), DrawPic und MLoadBMP intern für transparente Bitmaps der Formate *.gif, *.ico, *cur, *.emf und *.wmf eine Hintergrundfarbe fest. Bislang wurde hier immer reines Weiß genommen. Das erschwert aber die transparente Darstellung, wenn das Motiv selber Weiß enthält und auf einem nicht weißen Hintergrund gezeichnet werden soll. Mit dieser Set-Funktion kann nun eine beliebige interne Hintergrundfarbe angegeben werden, die im Motiv nicht vorkommt sollte. Nutzt man diese beim transparenen zeichnen, z.B. mit DrawPic, als Transparenzfarbe, wird das Motiv komplett wiedergegeben.

bitmaphandle& = create("hNewPic", dx%, dy%, farbe%)

dx%, dy%: Breite und Höhe der neuen Bitmap

farbe%: Farbe der Bitmap

Ergebnis: Handle der erzeugten Bitmap

Es wird eine neue Bitmap erzeugt mit der gewählten Hintergrundfarbe erzeugt. Die Bitmap wird im Speicher ezeugt.

StartPaint / EndPaint erweitert

Bisher konnte als StartPaint nur das Handle eines Fensters angegeben werden oder -1 für die mit MCLS erstellte Speicherbitmap bzw. die mit MLoadBmp in den Speicher geladene Bitmap. Zusätzlich kann nun ein Bitmap-Handle, Devicekontext oder Metafile-Devicekontext angegeben werden:

1. StartPaint hWnd% (Die Grafikbefehle werden auf das Fenster mit dem Handle hWnd% umgeleitet)

2. StartPaint -1 (Es wird auf der Speicherbitmap gezeichnet)

3. StartPaint hBmp& (Es wird auf die Bitrmap mit dem Handle hBmp& gezeichnet)

4. StartPaint DC& (Es wird auf den Devicekontext DC& gezeichnet)

5. StartPaint MetaDC& (Es wird auf den MetaFile-Devicekontext MetaDC& gezeichnet)

Die Systemvariablen %BmpCol, %BmpX und %BmpY werden erweitert, %BmpB hinzugefügt

Bislang wurden diese Systemvariablen nur bei LoadBmp, LoadSizedBmp und MLoadBmp korrekt gesetzt. Ab sofort werden sie auch bei DrawPic, DrawSizedPic, CopyPic, CopySizedPic, create("hPic",...), create("hSizedPic",...) und create("hPicCopy",...) gesetzt. Neu ist auch, dass im Fehlerfall, wenn das Bild nicht geladen bzw. erzeugt werden konnte, die Systemvariablen auf 0 zurückgesetzt werden. Neu ist die Systemvariable %BmpB, die die Farbtiefe (Bits pro Pixel) angibt. Hinweis: Auch bei 32 Bits/Pixel werden nur 24 Bit für die Farbinformation genutzt, nämlich je 8 Bit für die drei Grundfarben rot, grün und blau. Bei Bildformaten mit Alphakanal werden die übrigen 8 Bits für diesen genutzt.

Die Transparenzfarbe bei transparenter Darstellung kann beliebig eingestellt werden

Alle Befehle, die die Modusangabe -1 für Transparenz erlauben, erlauben nun einen weiteren Parameter, der die transparente erscheinende Farbe angibt. Wird dieser weggelassen, wird wie bisher die Farbe des untersten linkesten Punktes genommen.

1. DrawPic hBitmap&, X%, Y%; modus%[, transcol&]

2. DrawPic "Datei", X%, Y%; modus%[, transcol&] (gilt natürlich auch für das nun veraltete LoadBmp)

3. DrawPic hDLL&, "Name", X%, Y%; modus%[, transcol&] (gilt natürlich auch für das nun veraltete LoadExtBmp)

4. ClipLoadBmp X,Y;N[, transcol&]

5. CopyPic H, X1,Y1 - X2,Y2 > X3,Y3; N[, transcol&]

6. CopyBmp X1,Y1 - X2,Y2 > X3,Y3; N[, transcol&]

7. MCopyBmp X1,Y1 - X2,Y2 > X3,Y3; N[, transcol&]

bitmaphandle& = create("hPicCopy", hPic&[, x%, y%, dx%, dy%])

hPic&:    Handle der Quell-Bitmap

x%, y%:   Linke obere Ecke in der Quellbtmap

dx%, dy%: Breite und Höhe des Ausschnitts

Ergebnis: Handle der erzeugten Bitmap

Es wird eine Kopie einer Bitmap erzeugt. Wenn die Parameter x%, y%, dx% und dy% angegeben sind besteht die Kopie lediglich aus dem so bezeichnerten Ausschnitt der Originalbitmap.

SavePic dateinname$, bitmaphandle& [,qualitaet%]

Das zum Bitmaphandle gehörige Bild wird als BMP, PNG oder JPG-Datei unter dem angegebenen Namen (ggf. mit Pfad) gespeichert:

Wenn die Dateiendung ".PNG" ist, wird das Bild als PNG-Datei gespeichert,

Wenn der dritte Parameter angegeben ist, wird das Bild als JPG-Datei in entsprechender Qualität (1 - 100) gespeichert. Je geringer die Qualität, desto geringer die Bildgröße. Ein guter Wert ist zwischen 75 und 80.

bitmaphandle& = create("hPic", 0, name$)

Neben dem internen Ressourcennamen "&MEMBMP" gibt es vier weitere vorbelegte Namen:

"&OGLBMP" - Es wird eine Bitmapkopie des aktuellen OpenGL-Bildschirms erstellt.

"&SCRBMP" - Es wird eine Bitmapkopie des aktuellen Hauptfensters erstellt.

"&DSKBMP" - Es wird eine Bitmapkopie des aktuellen Desktops erstellt.

"&CLPBMP" - Es wird eine Bitmapkopie der aktuellen Zwischenablage erstellt, wenn diese eine Bitmap enthält.

bitmaphandle& = create("hPic", -1, dateiname$)

Es können jetzt auch Bilder im PNG-Format geladen werden. Die Dateiendung muß ".PNG" sein.

DrawIcon erweitert

Da die Befehle DrawIcon, DrawSysIcon, DrawLibIcon und DrawExtIcon letztlich nichts anderes machen als der Befehl DrawIcon, nämlich ein Icon auf den Bildschirm zu zeichen, habe ich sie alle zu DrawIcon zusammengefasst und bei der Gelegenheit gleich erweitert, um ein Icon aufgrund des Handles zu zeichnen oder ein Bild aus einer ImageList:

1. DrawIcon "Name", X%, Y% bleibt wie bisher

2. DrawIcon "Datei", Nr%, X%, Y% entspricht dem bisherigen DrawLibIcon

3. DrawIcon hDLL&, "Name", X%, Y% entspricht dem bisherigen DrawExtIcon

4. DrawIcon Nr%, X%, Y%  entspricht dem bisherigen DrawSysIcon

5. DrawIcon hIcon&, X%, Y% zeichnet das Icon mit dem Iconhandle hIcon&

6. DrawIcon hImageList&, Nr%, X%, Y% zeichnen Image Nr% aus der ImageList

Natürlich kommen Interpreter und Compiler auch noch mit den alten Formen klar: Alle DrawXxxIcon-Befehle werden nun gleich behandelt wie DrawIcon. (Und DrawImage wird erst gar nicht in die neue Version eingeführt.)

DrawPic und DrawSizedPic erweitert

Da die Befehle DrawPic, LoadsBmp und LoadExtBmp auf der einen und DrawSizedPic, LoadSizedBmp und LoadSizedExtBmp auf der anderen Seite letztlich das Gleiche machen, nämlich eine Bitmap in Originalgröße ider scaliert anzeigen, habe ich sie zu DrawPic bzw. DrawSizedPic zusammengefasst:

1. DrawPic hBitmap&, X%, Y%; modus% bleibt wie bisher

2. DrawPic "Datei", X%, Y%; modus%  entspricht dem bisherigen LoadBmp mit dem Unterschied, dass alle in Create("hPic", -1, "Name") unterstützen Formate geladen und angezeigt werden

3. DrawPic hDLL&, "Name", X%, Y%; modus% entspricht dem bisherigen DrawExtBmp

4. DrawSizedPic hBitmap&, X%, Y% - DX%, DY%; modus% bleibt wie bisher

5. DrawSizedPic "Datei", X%, Y% - DX%, DY%; modus%  entspricht dem bisherigen LoadSizedBmp mit dem Unterschied, dass alle in Create("hPic", -1, "Name") unterstützen Formate geladen und angezeigt werden

6. DrawSizedPic hDLL&, "Name", X%, Y% - DX%, DY%; modus% entspricht dem bisherigen DrawSizedExtBmp

Auch hier kommen Interpreter und Compiler auch noch mit den alten Formen klar.


Metafiles

XProfan unterstützt ab Version 11 auf das MetaFile-Format. Es wird sowohl das ältere Format (Windows MetaFile *.wmf) als auch das neuere erweiterte Format (Enhanced MetaFile *.emf) erkannt.

Mit Create("hPic",..) und anderen Bitmapbefehlen können zwar auch Metafiles (*.wmf und *.emf) geladen werden, allerdings werden hier die Grafiken in Bitmaps umgewandelt, die die Originalgröße der urspünglichen Metafiles haben. Das ist auch weiterhin sinnvoll, wenn man von Grafiken, die als Metafiles vorliegen, ein Bitmaphandle benötigt, etwa um sie in Toolbars oder auf Buttons zu verwenden.

MetaFiles sind aber keine Bitmaps, sondern lediglich Befehlsfolgen, um Grafiken zu zeichnen, so wie es z.B. auch bei Vektororientierten Grafikprogrammen, wie etwa CAD und Publikations-Software üblich ist. Da die Grafiken in beliebiger Größe gezeichnet werden können, bleiben Sie auch scharf, wenn sie größer gezeichnet werden, als die Ursprungsgröße war. Bei Bitmaps hingegen werden bei Vergrößerung nur die Pixel größer und deutlicher sichtbar.

hMeta& = create("hMetaFile", Datei$)

Datei$ : Dateiname eines MetaFiles (*.wmf oder *.emf).

Ergebnis : Handle des MetaFiles

Das MetaFile wird geladen und das entsprechende Handle zurückgeliefert. Nach Gebrauch muss das Handle wieder mit DeleteObject entsorgt werden. Die alten Windows-MetaFiles (*.wmf) werden automatisch in die neueren Enhanced MetaFiles umgewandelt. Die Systemvariablen %MFX und %MFY enthalten die ursprüngliche Größe der Grafik.

PlayMetaFile hMeta&, x%, y%, dx%, dy%

hMeta& : MetaFile-Handle

x%, y% : linke obere Ecke

dx%, dy% : Größe der Grafik

Das MetaFile wird auf den Bildschirm (oder Drucker) gezeichnet.

Der Befehl DeleteObject wurde erweitert, um auch Handles von Enhanced MetaFiles löschen zu können. Die Funktion ClassOf liefert beim Handle eines MetaFiles "MetaFile" zurück.

Ein Beispiel:

CLS

' Laden und Anzeigen eines Metafiles

var hMeta& = create("hMetaFile", "floorplan.emf")

playMetaFile hMeta&, 10, 10, 800, 600

WaitInput

deleteObject hMeta&

END

mfDC& = newMetaFile(Datei$, Titel$, dx%, dy%)

Datei$ : Dateiname des zu erzeugenden MetaFiles (*.emf)

Titel$ : Titel der Grafik

dx%, dy% : Ursprungsgröße der Grafik

Ergebis : DeviceContext des neuen MetaFiles

Ein neues leeres MetaFile wird angelegt. Mit StartPaint mfDC& können nun die Grafikausgaben auf das MetaFile umgeleitet werden. Alle Zeichenoperationen auf den DeviceContext des MetaFiles werden im MetaFile aufgezeichnet.

Der Dateiname kann auch leer sein. Dann wird das MetaFile nur im Speicher erzeugt und geht bei Zerstören des Handles verloren.

hMeta& = closeMetaFile(mfDC&)

mfDC& : MetaFile DeviceContext

Ergebnis : Handle des MetaFiles

Die Aufzeichnung des MetaFiles wird beendet und die Datei auf den Datenträger geschrieben. Der Rückgabewert ist das Handle des Metafiles und kann zur Anzeige der Grafik verwandt werden.

Ein weiteres Beispiel:

CLS

' Erzeugen eines neuen Metafiles

var DCMeta& = newMetaFile("c:\\plaene\kueche.emf", "Küche", 800, 600)

StartPaint DCMeta&

    <hier wird der Küchenplan gezeichnet>

EndPaint

' Aufzeichnung beenden und Datei abspeichern

var hMeta& = closeMetaFile(DCMeta&)

' Anzeigen

playMetaFile hMeta&, 10, 10, 800, 600

WaitInput

deleteObject hMeta&

END


TrayIcons

TrayIcon("Add", hWnd&, ID%, hIcon&, Text$)

hWnd&: Das Fenster, zu dem das TrayIcon gehört

ID%: Die Nummer des TrayIcons. Ein Fenster kann mehrere TrayIcons haben. Jedes TrayIcon des Programmes muß eine eindeutige Nummer haben.

hIcon&: Icon-Handle

Text$: Tooltiptext des TrayIcons

Es wird ein TrayIcon erzeugt. Da das TrayIcon wissen muß, welches Fenster bei einem Klicken zu benachrichtigen ist, muß diese angegeben werden.

TrayIcon("SetIcon", hWnd&, ID%, hIcon&)

hWnd&: Das Fenster, zu dem das TrayIcon gehört

ID%: Die Nummer des TrayIcons

hIcon&: Icon-Handle

Das Icon des TrayIcons wird ersetzt.

TrayIcon("SetTip", hWnd&, ID%, Text$)

hWnd&: Das Fenster, zu dem das TrayIcon gehört

ID%: Die Nummer des TrayIcons

Text$: Tooltiptext des TrayIcons

Das Text des TrayIcons wird ersetzt.

TrayIcon("Delete", hWnd&, ID%)

hWnd&: Das Fenster, zu dem das TrayIcon gehört

ID%: Die Nummer des TrayIcons

Das TrayIcon wird gelöscht.

TrayMsg% = TrayIcon("Msg", ID%)

ID%: Die Nummer des TrayIcons

Nach WaitInput kann mit dieser Funktion abgefragt werden, ob ein Klick auf das Trayicon mit der Nummer ID% das Verlassen des WaitInput ausgelöst hat. In diesem Fall ist das Ergebnis der Funktion größer als 0:

$201 (513): Linke Maustaste gedrückt

$202 (514): Linke Maustaste losgelassen

$203 (515): Doppelklick mit linker Maustaste

$204 (516): Rechte Maustaste gedrückt

$205 (517): Rechte Maustaste losgelassen

$206 (518): Doppelklick mit rechter Maustaste

$207 (519): Mittlere Maustaste gedrückt

$208 (520): Mittlere Maustaste losgelassen

$209 (521): Doppelklick mit mittlerer Maustaste

Ein minimalistisches Beispielprogramm:

Declare hIcon&, ende%

WindowTitle "XProfan 11: TrayIcon-Demo"

Cls

hIcon& = create("hIcon", "A")

TrayIcon("add", %hWnd, 1, hIcon&, "XProfan 11")

Print "Ende mit linkem Doppelklick!"

WhileNot ende%

   WaitInput

   Select TrayIcon("msg", 1)

     CaseOf $201: Print "Linke Maustaste"

     CaseOf $203: ende% = 1

     CaseOf $204: Print "Rechte Maustaste"

     CaseOf $206: Print "Rechter Doppelklick"

   EndSelect

EndWhile

TrayIcon("delete", %hWnd, 1)

DeleteObject hIcon&

End


Kollisionen und Überschneidungen

n vielen Spielenm ist die Kollisionskontrolle sehr wichtig. Es wird überprüft, ob sich zwei Spielelemente überschneiden. Auch wenn sich das in ein paar Programmzeilen schon jetzt programmieren läßt, bietet XProfan 11 einige Funktionen, die das Ganze noch erheblich beschleuinigen:

between(x!, vonx!, bisx!)

x!: Double

vonx!: Double

bisx!: Double

Ergebnis: 0 oder 1

Das Ergebnis ist 1, wenn sich der Wert von x! nicht kleiner als vonx und nicht größer als bisx! ist.

between(x!, vonx!, bisx!, y!, vony!, bisy!)

x!: Double

vonx!: Double

bisx!: Double

y!: Double

vony!: Double

bisy!: Double

Ergebnis: 0 oder 1

Das Ergebnis ist 1, wenn sich der Punkt x!/y! innerhalb der rechtwinkligen Fläche befindet, die durch die Koordinaten vonx!/vony! und bisx!/bisy! beschrieben wird.

between(x!, vonx!, bisx!, y!, vony!, bisy!, z!, vonz!, bisz!)

x!: Double

vonx!: Double

bisx!: Double

y!: Double

vony!: Double

bisy!: Double

z!: Double

vonz!: Double

bisz!: Double

Ergebnis: 0 oder 1

Das Ergebnis ist 1, wenn sich der Punkt x!/y!/z! innerhalb des rechtwinkligen Raumes (Quader) befindet, der durch die Koordinaten vonx!/vony!/vonz! und bisx!/bisy!/bisz! beschrieben wird.

range(wert!, von!, bis!)

wert!: Double

von!: Double

bis!: Double

Ergebnis: Double

Das Ergebnis ist der Wert in den angegebenen Grenzen. Ist der Wert also kleiner als von, wird er auf von gesetzt, ist er größer als bis, wird er auf bis gesetzt. So wird sichergestellt, dass sich der Wert vor der Weiterverarbeitung innerhalb der angegebenen Grenzen bewegt.

Oftmals reicht es jedoch nicht aus, einen bestimmten Punkt zu überprüfen, sondern man möchte wissen, ob ein Objekt ein anderes berührt oder überschneidet. Auch hierfür gibt es eine neue Funktion:

intersect(typ$, objekt1#, objekt2#)

typ$: Objekttyp - "r" = Rechteck / "c" = Quader / "d" = Kreis / "s" = Kugel / "o" = oGLSprite

objekt1#: Fläche, Quader, Kreis, Kugel oder oGLSprite

objekt2#: Fläche, Quader, Kreis, Kugel oder oGLSprite

Ergebnis: 0 oder 1

Das Ergebnis ist 1, wenn sich die Objekte berühren oder überschneiden.

Beide Objekte müssen vom angegebenen Objektyp sein oder abgeleitet sein. Für Rechteck, Quader, Kreis und Kugel gibt es vier neue vordefinierte Strukturen in XProfan 11:

xpRect   = posx!, posy!, x!, y!

xpCuboid = posx!, posy!, posz!, x!, y!, z!

xpDisk   = posx!, posy!, x!

xpSphere = posx!, posy!, posz!, x!

Die Eigenschaften ensprechen den gleichnamigen Eigenschaften der schon seit XProfan 10 bekannten vordefinierten Struktur oGLSprite. Deshalb auch das x! für den Radius bei Kreis und Kugel.

Bei den oGLSprites werden alle dreidimensionalen Objekte (natürlich nur rechnerisch) zu Quadern vereinfacht und alle zweidimensionalen Objekte zu Rechtecken. Das ist für nahezu alle Fälle völlig ausreichend und der Spieler wird davon nichts merken. Damit auch ein oGL-Sprite des Typs "LIST" wie ein dreidimensionales Objekt überprüft werden kann, sollte man darauf achten, dass der Bezugspunkt für die Position dem eingestellten Modus entspricht (siehe: ogl("SetPosMode", n%)) und man die Eigenschaften x& und y& mit der Größe des Sprites befüllt. Bei den zweidimensionalen Objekten (QUAD, TRIANGLE, DISK und PARTIALDISK) wird posz& für den Vergleich als 0 gewertet. Das ermöglicht auch die Kollisionsabfrage bei 2D-Spielen, die mehrere Ebenen (verschiedene Positionen auf der Z-Achse) benutzen.

Ein minimalistischer Beispielcode:

   var r1# = new(xpRect)

   var r2# = new(xpRect)

   r1#.posx! = 0

   r1#.posy! = 0

   r1#.x! = 10

   r1#.y! = 10

   r2#.posx! = 10

   r2#.posy! = 10

   r2#.x! = 10

   r2#.y! = 10

   print intersect("r", r1#, r2#)


Zusätzliche Formatierungsmöglichkeiten bei Format$

Der Formatstring kann nun auch einen Formatbezeichner in der von C und C++ her bekannte Form aufweisen:

"%[-][ b][.d]t"

Der Formatbezeichner beginnt mit dem Zeichen %. Alle Zeichen davor werden so übernommen, wie sie im Formatstring stehen. Auf das Prozentzeichen folgt eine der nachstehenden Angaben (in der aufgeführten Reihenfolge):

  • Eine optionale Angabe für die linksbündige Ausrichtung: [-]
  • Eine optionale Breitenangabe: [ b]
  • Eine optionale Genauigkeitsangabe: [.d]
  • Das Zeichen für den Konvertierungstyp: t

Wird keine entsprechende Formatierungsangabe im Formatstring gefunden, wird die Formatierung wie bisher ausgeführt. Es sind also beide Varianten möglich!

Folgede Werte für t sind möglich:

d: Dezimal. Das Argument muss ein Integerwert sein. Der Wert wird in einen String umgewandelt, der aus Dezimalzahlen besteht. Wenn der Format-String einen Bezeichner für die Genauigkeit enthält, muss der resultierende String mindestens die angegebene Anzahl von Stellen aufweisen. Enthält er weniger Stellen, wird der String auf der linken Seite mit Nullen aufgefüllt.

u: Vorzeichenlose Dezimalzahl. Das Format "u" entspricht dem Format "d", allerdings enthält der resultierende String kein Vorzeichen; der Integer wird als vorzeichenloser Integer interpretiert.

e: Wissenschaftliche Notation. Das Argument muss ein Gleitkommawert sein. Der Wert wird in einen String mit der folgenden Form umgewandelt: "-d,ddd...E+ddd". Wenn es sich um eine negative Zahl handelt, beginnt der String mit einem Minuszeichen. Vor dem Dezimaltrennzeichen steht immer eine Ziffer. Die Gesamtzahl der Stellen im Ergebnis-String (einschließlich der Ziffer vor dem Dezimalkomma) wird durch den Genauigkeitsbezeichner im Format-String festgelegt. Ist dieser nicht vorhanden, wird eine vorgegebene Genauigkeit von 15 Stellen angenommen. Auf den Exponenten "E" im String folgen immer ein Plus- oder Minuszeichen und mindestens drei Stellen.

f: Fest. Das Argument muss ein Gleitkommawert sein. Der Wert wird in einen String der folgenden Form umgewandelt: "-ddd.ddd...". Wenn es sich um eine negative Zahl handelt, beginnt der String mit einem Minuszeichen. Die Anzahl der Stellen nach dem Dezimalkomma wird durch den Genauigkeitsbezeichner im Format-String festgelegt. Ist dieser nicht vorhanden, wird eine vorgegebene Genauigkeit von zwei Dezimalstellen verwendet.

g: Allgemein. Das Argument muss ein Gleitkommawert sein. Der Wert wird unter Verwendung des Formats Fest oder Wissenschaftliche Notation in den kürzestmöglichen Dezimal-String umgewandelt. Die Anzahl der signifikanten Stellen im resultierenden String wird durch den Genauigkeitsbezeichner im Format-String festgelegt. Ist dieser nicht vorhanden, wird eine vorgegebene Genauigkeit von 15 Stellen angenommen. Nachfolgende Nullen werden aus dem resultierenden String entfernt. Ein Dezimaltrennzeichen wird nur bei Bedarf angezeigt. Für den resultierenden String wird das Festkommaformat verwendet, wenn die Anzahl der Stellen vor dem Dezimaltrennzeichen kleiner oder gleich der festgelegten Genauigkeit und der Wert größer oder gleich 0,00001 ist. In allen anderen Fällen wird die wissenschaftliche Notation benutzt.

n: Zahl. Das Argument muss ein Gleitkommawert sein. Der Wert wird in einen String der folgenden Form umgewandelt: "-d,ddd,ddd.ddd...". Das Format "n" entspricht dem Format "f", allerdings enthält der resultierende String Tausendertrennzeichen.

m: Währung. Das Argument muss ein Gleitkommawert sein. Der Wert wird in einen String umgewandelt, der einen Währungsbetrag darstellt. Die Währung wird gemäß der Systemeinstellungen dargestellt. Enthält der Format-String einen Genauigkeitsbezeichner, setzt dieser den Wert in der Systemsteuerung für diese Formatierung außer Kraft.

p: Zeiger. Das Argument muss ein Zeigerwert (LongInt) sein. Der Wert wird in einen String mit acht Zeichen, der den hexadezimalen Wert des Zeigers darstellt, konvertiert.

x: Hexadezimal. Das Argument muss ein Integerwert sein. Der Wert wird in einen String umgewandelt, der aus hexadezimalen Stellen besteht. Wenn der Format-String einen Bezeichner für die Genauigkeit enthält, muss der resultierende String mindestens die angegebene Anzahl von Stellen aufweisen. Enthält er weniger Stellen, wird der String auf der linken Seite mit Nullen aufgefüllt.

Der Konvertierungstyp kann in Klein- oder Großschreibung angegeben werden. Zeichen die vor dem Prozentzeichen und/oder hinter dem Typkennzeichen stehen, werden unverändert in den Ergebnisstring übernommen. Etwaige Prozentzeichen müssen doppelt geschrieben werden.

Beispiel:

   cls

   var test! = 1.4567

   var int& = 456789

   ' alte Form wie bisher

   print format$("Test: 0000#.## % Rendite", test!) + "<"

   ' neue  Form

   print format$("Test: %8.2f %% Rendite", test!) + "<"

   print format$("%8.2f", test!) + "<"

   print format$("%-8.2f", test!) + "<"

   print format$("%e", test!) + "<"

   print format$("%g", test!) + "<"

   print format$("%m", test!) + "<"

   print format$("%20x", int&) + "<"

   print format$("%9u", -int&) + "<"

   print format$("%9d", -int&) + "<"

   print format$("%p", int&) + "<"

   waitinput

   end


TreeView

Hier gilt mein besonderer Dank Jörg Sellmeyer, dessen TreeView-Unit mir Inspiration und Vorlage für die Implementierung der TreeViews in XProfan war.

Ein TreeView ist eine Baumansicht von Texten und/oder Bildern, wobei die einzelnen Zweige dieser Ansicht auf- und zugeklappt werden können. Wie üblich, wird auch dieses Dialogelement mit einer Create-Funktion erstellt:

hTV& = Create("TreeView", hWnd&, hIL&, X%, Y%, DX%, DY%)

hWnd&: Handle des übergeordneten Fensters/Dialogs

hIL&: Handle einer ImageList oder 0, wenn es keine Icons geben soll

X%,Y%: Position des TreeView

DX%,DY%: Größe des TreeView

Ergebnis: Handle des TreeView

Folgende Funktionen gibt es, um das TreeView zu füllen und zu bearbeiten:

Item% = TreeView("InsertItem", hTV&, Parent%, Image%, Text$)

hTV&: Handle des TreeView

Parent%: Übergeordneter Eintrag. In der Wurzelebene ist der Wert 0.

Image%: Nummer des Bildes, wenn eine ImageList angegeben wurde

Text$: Der Text des Eintrags

Ergebnis: Handle des Eintrags

Diese Funktion fügt dem TreeView einen Eintrag hinzu. Wird bei Parent ein Handle eines Eintrags angegeben, wird der neue Eintrag unter diesen angehängt. Damit der Eintrag durch Aufklappen des übergeordneten Eintrags sichtbar werden kann, muss diesem mittels SetChildren noch mitgeteilt werden, dass er Kinder hat.

TreeView("SetItemImage", hTV&, Item%, Image%)

hTV&: Handle des TreeView

Item%: Handle des Eintrags

Image% : Nummer des Bildes, wenn eine ImageList angegeben wurde

Das Bild des Eintrages wird verändert

TreeView("SetChildren", hTV&, Item%, Wert%)

hTV&: Handle des TreeView

Item%: Handle des Eintrags

Wert%: 1 - Eintrag hat Kinder / 0 - Eintrag hat keine Kinder

Es wird bestimmt, ob ein Eintrag untergeordnete Einträge (Kinder) hat.

TreeView("Expand", hTV&, Item%, Wert%)

hTV&: Handle des TreeView

Item%: Handle des Eintrags

Wert%: Aktion (1, 2 oder 3)

Wenn der Eintrag Kinder hat, kann er auf- oder zugeklappt werden:

1 - Der Eintrag wird zugeklappt

2 - Der Eintrag wird aufgeklappt

3 - Der Eintrag wird aufgeklappt, wenn er zuvor zugeklappt ist und umgekehrt

Item% = TreeView("GetSelected", hTV&)

hTV&: Handle des TreeView

Ergebnis: Der selektierte Eintrag

Mit dieser Funktion wird ermittelt, welcher Eintrag augenblicklich selektiert ist.

PItem% = TreeView("GetParent", hTV&, Item%)

hTV&: Handle des TreeView

Item%: Handle des Eintrags

Ergebnis: Handle des übergeordneten Items

Mit dieser Funktion wird das übergeordnete Item eines Items ermittelt. Gibt es kein übergeordnetes Item, ist das Ergebnis 0.

TreeView("Select", hTV&, Item&)

hTV&: Handle des TreeView

Item%: Handle des Eintrags

Mit dieser Funktion wird ein Eintrag selektiert unf gegebenenfalls in den sichtbaren Bereich gescrollt.

TreeView("DeleteItem", hTV&, Item%)

hTV&: Handle des TreeView

Item%: Handle des Eintrags

Der Eintrag Item% wird aus dem Treeview entfernt. Hat der Eintrag Kinder, werden auch diese entfernt.

Auch SetText und GetText$() wurden erweitert, um mit einem TreeView umgehen zu können:

SetText hTV&, Item%, Text$

hTV& : Handle des TreeView

Item%: Handle des Eintrags

Text$: Der Text des Eintrags

Der Text des Eintrags wird mit Text$ überschrieben.

Text$ = GetText$(hTV&, Item&)

hTV&: Handle des TreeView

Item%: Handle des Eintrags

Ergebnis: Text des Eintrags

Mit dieser Funktion wird der Text des Eintrags ermittelt.

Die Gesamtzahl aller Einträge eines TreeView lässt sich mit GetCount(hTV&) ermitteln.

Ein Beispielprogramm für ein TreeView mit ImageList:

CLS

var hToolBmp& = create("hPic", 0, "TOOLBAR")

var hIL& = Create("ImageList", 16, 16, hToolBmp&, rgb(192, 192, 192))

var hTV& = Create("TreeView",%hwnd,hIL&,420,40,200,(height(%HWnd)-70))

DeleteObject hToolBmp&  ' wird nicht mehr benötigt

print "Profan-Kringel: ";ImageList("AddIcon", hIL&, create("hIcon", "A"));""

print "Klassen: ";classof(hTV&);tab(24);classof(hIL&)

print "Handles: ";hTV&;tab(24);hIL&

var HeaderEbene& = TreeView("InsertItem",hTV&, 0,0,"Includes/Header")

var ProcEbene& = TreeView("InsertItem",hTV&, 0,1,"Prozeduren")

var StrukturEbene& = TreeView("InsertItem",hTV&, 0,2,"Strukturen/Klassen")

var KommentarEbene& = TreeView("InsertItem",hTV&, 0,3,"Kommentare/Faltungen")

TreeView("InsertItem",hTV&, HeaderEbene&,4,"Text 1")

TreeView("InsertItem",hTV&, ProcEbene&,5,"Text 2")

TreeView("InsertItem",hTV&, StrukturEbene&,6,"Text 3")

TreeView("InsertItem",hTV&, KommentarEbene&,7,"Text 4")

TreeView("InsertItem",hTV&, KommentarEbene&,52,"Text 5")

var T6& = TreeView("InsertItem",hTV&, KommentarEbene&,9,"Text 6")

TreeView("InsertItem",hTV&, T6&,10,"Text 7")

TreeView("SetItemImage",hTV&, KommentarEbene&,10)

TreeView("SetChildren",hTV&, Procebene&,1)

TreeView("SetChildren",hTV&, HeaderEbene&,1)

TreeView("SetChildren",hTV&, StrukturEbene&,1)

TreeView("SetChildren",hTV&, KommentarEbene&,1)

TreeView("SetChildren",hTV&, T6&,1)

TreeView("Expand",hTV&, ProcEbene&, 2)

SetText hTV&, HeaderEbene&,"Includes/Header (2)"

SetText hTV&, ProcEbene&,"Prozeduren (3)"

SetText hTV&, StrukturEbene&,"Strukturen/Klassen (4)"

SetText hTV&, KommentarEbene&,"Kommentare/Faltungen (99)"

TreeView("Select",hTV&, T6&)

print "Anzahl:  ";GetCount(hTV&);tab(24);getCount(hIL&)

DrawIcon hIL&, 10, 420, 15

var ende% = 0

var item& = 0

whilenot ende%

  waitinput

  if %key = 27

    ende% = 1

  elseif clicked(hTV&)

    item& = TreeView("GetSelected", hTV&)

    print GetText$(hTV&, item&);".";GetText$(hTV&, TreeView("GetParent", hTV&, item&))

  endif

endwhile

end


ImageList

Um Bilder in Controls, wie z.B. dem TreeView anzuzeigen, benötigt man eine ImageList (Bilderliste). Diese wird mit folgender Create-Funktion erzeugt:

hIL& = Create("ImageList", DX%, DY%[, hBmp&[, tCol%]])

DX%,DY%: Größe der einzelnen Bilder

hBmp&: Bitmaphandle der Bitmap, die Bilder enthält.

tCol&: Transparente Farbe

Ergebnis: Handle der ImageList

Die verwandte Bitmap muss mindestens die passende Größe für alle im Programm vorkommenden TreeView-Bilder haben: Die Höhe der Bitmap entspricht dem Wert DX%, die Breite der Bitmap dem Wert DY% multipliziert mit der Anzahl der Bilder. Die Anzahl der Bilder, die eine ImageList enthält, lässt sich mit GetCount(hIL&) ermitteln. tCol% gibt die Farbe der Bitmap an, die Transparent dargestellt werden soll. In der Regel ist des die Hintergrundfarbe der Bilder. Wird dieser Wert weggelassen, werden die Bilder ohne Transparenz dargestellt. Wird auch kein Bitmaphandle angegeben, wird eine leere Imagelist erzeugt.

Folgende Funktionen gibt es, um eine ImageList zu füllen und zu bearbeiten:

ImageList("Add", hIL&, hBmp&[, tCol%])

hIL&: Handle der ImageList

hBmp&: Bitmaphandle der Bitmap, die Bilder enthält.

tCol&: Transparente Farbe

Ergebnis: Nummer des ersten hunzugefügten Images

Die Bitmap hBmp& wird der ImageList hinzugefügt. Sollen die Bilder transparent dargestellt werden, ist mit tCol% die Farbe anzugeben, die transparent sein soll.

ImageList("AddIcon", hIL&, hIcon&)

hIL&: Handle der ImageList

hIcon&: Handle des Icons oder Cursors, das als Image hinzugefügt werden soll

Ergebnis: Nummer des ersten hunzugefügten Images

Das Icon oder der Cursor mit dem Handle hIcon& wird der ImageList hinzugefügt.

hIcon& = ImageList("GetIcon", hIL&, Image%)

hIL&: Handle der ImageList

Image%: Nummer des Bildes, das als Icon oder Cursor verwandt werden soll

Ergebnis: Handle des erzeugten Icons/Cursors

Das Bild Image% aus der Imagelist wird zu einem Icon bzw. Cursor.

ImageList("Destroy", hIL&)

hIL&: Handle der ImageList

Die Imagelist wird zerstört. Da die Imagelist Bitmaps enthält, die unter Umständen recht viel Speicherplatz benötigen, es es wichtig, eine ImageList nach Gebrauch korrekt zu entsorgen. Die ImageList darf natürlich nicht zerstört werden, solange noch Dialogelemente auf dem Bildschirm sind, die diese benötigen.

Es gibt auch eine neue Variante des DrawIcon-Befehles, um ein einzelnes Bild einer ImageList anzuzeigen:

DrawIcon hIL&, Image%, X%, Y%

hIL&: Handle der ImageList

Image%: Nummer des Bildes

X%,Y%: Position, an der gezeichnet wird

Ein Beispielprogramm, dass eine ImageList im TreeView benutzt, findet sich hier .

Die Funktionen Create("TabCtrl",...) und Create("ToolBar",...) wurden auch erweitert, um mit einer ImageList umzugehen.


Browser: HTMLWin

Das WebControl "HTMLWin" dient zum Anzeigen von HTML-Seiten, egal ob lokal auf der Platte oder im Internet. Es nutzt das gleiche Control wie der Internet-Explorer. Wie üblich, wird auch dieses Dialogelement mit einer Create-Funktion erstellt:

hWC& = Create("HTMLWin", hWnd&, Ziel$, modus%, X%, Y%, DX%, DY%)

hWnd&: Handle des übergeordneten Fensters/Dialogs

Ziel$: Internetadresse, Ordner, HTML-Datei oder HTML-Quelltext, die/der angezeigt werden soll.

modus%: Anzeigeoptionen

X%,Y%: Position des HTML-Fensters

DX%,DY%: Größe des HTML-Fensters

Ergebnis: Handle des HTML-Fensters

Derzeit gibt es zwei Anzeigeoptionen: 0 = randlos, 1 = mit dem üblichen 3D-Rand.

Es gibt auch einige Funktionen, die es z.B. ermöglichen, durch die HTML-Seiten zu navigieren:

HTMLWin("GoBack", hWC&)

hWC&: Handle des HTML-Fensters

Diese Funktion entspricht dem Zurück-Button des Explorers: Es wird die vorherige HTML-Seite angezeigt.

HTMLWin("GoForward", hWC&)

hWC&: Handle des HTML-Fensters

Nach einem "GoBack" kann man mit dieser Funktion wieder vorwärts blättern.

HTMLWin("GoHome", hWC&)

hWC&: Handle des HTML-Fensters

Es wird die in den Internetoptionen des Betriebssystems eingestellte Startseite aufgerufen.

HTMLWin("GoSearch", hWC&)

hWC&: Handle des HTML-Fensters

Es wird die in den Internetoptionen des Betriebssystems eingestellte Suchseite aufgerufen.

HTMLWin("Stop", hWC&)

hWC&: Handle des HTML-Fensters

Das Laden der aktuellen Seite wird abgebrochen.

HTMLWin("Refresh", hWC&)

hWC&: Handle des HTML-Fensters

Die aktuelle Seite wird erneut geladen.

HTMLWin("Navigate", hWC&, Ziel$)

hWC&: Handle des HTML-Fensters

Ziel$: Internetadresse, Ordner, HTML-Datei oder HTML-Quelltext, die/der angezeigt werden soll.

Es wird neues Tiel gewählt und angezeigt.

busy% = HTMLWin("GetBusy", hWC&)

hWC&: Handle des HTML-Fensters

Ergebnis: 0, wenn das Control inaktiv ist; Größer als 0 , wenn das Control noch aktiv ist.

Mit dieser Funktion kann überprüft werden, ob das Control mit dem Laden und Darstellen einer Seite fertig ist.

Für sehr fortgeschrittene XProfaner gibt es noch eine weitere Funktion:

IFace# = HTMLWin("QueryInterface", hWC&)

hWC&: Handle des HTML-Fensters

Ergebnis: Pointer auf das Interface des HTML-Controls.

Die Bereichsvariable IFace# muß die passende Größe haben. Ein Beispiel für die Anwendung liegt in QueryInterface.prf vor.

Folgendes Listing zeigt einen Mini-Web-Browser:

usermessages 16

declare WebControl&, a&, a$

'---- Fenster + Menü ----

cls

appendmenubar 101,"<"

appendmenubar 102,">"

appendmenubar 103,"&X"

appendmenubar 104,"&Aktualisieren"

appendmenubar 105,"&Startseite"

appendmenubar 106,"&Suchen"

appendmenubar 107,"&Datei öffnen"

appendmenubar 108,"&Ordner öffnen"

appendmenubar 109,"&URL öffnen"

'---- WebControl erzeugen ----

WebControl&=create("HTMLWin",%hwnd,"www.xprofan.de",0,0,0,width(%hwnd),height(%hwnd))

'---- Programmschleife ----

while 1

  if %key=4

    setwindowpos WebControl&=0,0-width(%hwnd),height(%hwnd)

  elseif menuitem(101)

    HTMLWin("GoBack", WebControl&)

  elseif menuitem(102)

    HTMLWin("GoForward", WebControl&)

  elseif menuitem(103)

    HTMLWin("Stop", WebControl&)

  elseif menuitem(104)

    HTMLWin("Refresh", WebControl&)

  elseif menuitem(105)

    HTMLWin("GoHome", WebControl&)

  elseif menuitem(106)

    HTMLWin("GoSearch", WebControl&)

  elseif menuitem(107)

    a$=loadfile$("Bitte Bild oder HTML-Datei wählen","*.*")

    repaint 1

    HTMLWin("Navigate", WebControl&, a$)

  elseif menuitem(108)

    a$=choosedir$("Bitte Ordner wählen")

    repaint 1

    HTMLWin("Navigate", WebControl&, a$)

  elseif menuitem(109)

    a$=input$("Bitte Web-Adresse eingeben:","URL:","")

    repaint 1

    HTMLWin("Navigate", WebControl&, a$)

  endif

  while HTMLWin("GetBusy", WebControl&)

    sleep 200

  endwhile

  waitinput

  case %umessage=16 : break

endwhile

destroywindow(WebControl&)

end


Subclassing mit XProfan

Was ist Subclassing?

Jedes Fenster oder Dialogelement in Windows hat eine Fensterprozedur, die die Messages verarbeitet, die an dieses Fenster geschickt werden. Diese Fensterprozedur reagiert entweder selbst auf diese Messages oder reicht sie direkt an Windows zur Verarbeitung weiter. Auf diese Fensterprozedur hat der XProfan-Programmierer in der Regel keinen Einfluß, außer durch das Setzen entsprechender Stile.

Mit dem SubClassing können wir die Fensterprozeduren oder die Verarbeitung der Messages durch Windows zwar auch nicht direkt verändern, aber wir können die Messages für ein (oder mehrere) Fenster ersteinmal zu einer eigenen Prozedur umleiten, in dem wir der Fensterklasse unsere eigene Prozedur als Fensterprozedur unterschieben. Darin können wir dann Messages, die uns interessieren abfangen und entsprechend reagieren. Und damit Windows nicht aus dem Takt kommt, leiten wir die Messages, die wir nicht oder nur teilweise bearbeiten zum Schluß an die Original Windowsprozedur weiter, so dass Windows diese wie üblich behandeln kann.

Subclassing mit XProfan

Um die Messages eines Fensters oder Dialogelementes umzuleiten, gibt es den Befehl SubClass:

SubClass hWnd&, n%

hWnd%: Handle des Fensters oder Dialogelementes

n%: Modus: 1 = Subclassing einschalten, 0 = Subclassing ausschalten

Sobald das Subclassing eingeschaltet ist, werden alle Messages zu unserer Subclassing-Prozedur umgeleitet.

In XProfan gibt es eine spezielle Prozedur für das das Bearbeiten der umgeleiteten Messages: SubClassProc

Wie alle Prozeduren wird auch diese mit EndProc beendet. Ganz wichtig hier der Befehl SetWinProc, denn dieser legt fest, ob anschließend die ursprüngliche Windowsprozedur noch aufgerufen wird oder nicht:

Set("WinProc", n%)

n%: 1 = WindowsProzedur wird aufgerufen, 0 = WindowsProzedur wird nicht aufgerufen

Normalerweise muß die Windowsprozedur für alle Fälle aufgerufen werden, die unsere Subclassing-Prozedur nicht abdeckt. Da "WinProc" defaultmäßig auf 1 gesetzt ist, sieht eine minimalistische SubclassingProzedur also so aus:

SubClassProc

EndProc

Diese Prozedur würde exakt nichts bewirken, da alle umgeleiteten Messages ungehört an die ursprüngliche WindowsProzedur weitergeleitet werden und dort wie gewohnt behandelt werden. Würden wir das WinProc mit Set("WinProc", 0) auf 0 setzen, würden alle mit SubClass umgeleiteten Fenster sofort einfrieren. Wäre darunter unser Hauptfenster, würde das Programm nur noch per Taskmanager zu beenden sein, da nun alle seine Messages ungehört in den Weiten unseres Speichers verhallen.

Damit man auf die umgeleiteten Messages reagieren kann, gibt es vier neue Systemvariablen, die nur innerhalb der SubClassProc gültig sind:

&sWnd: Handle des Fensters, an das die Message gerichtet ist. Das ist wichtig, wenn wir mehrere Fenster/Dialogelemente Subclassen.

%sMessage: Umgeleitete Message

&sWParam: Der WParam-Wert der Message

&sLParam: Der LParam-Wert der Message

Und damit das Abfragen etwas einfacher wird gibt es eine neue Funktion:

n% = SubClassMessage(hWnd&, MsgNr%)

hWnd&: Fensterhandle

MsgNr%: MessageNummer

Ergebnis: 0 oder 1

Diese Funktion gibt 1 zurück, wenn die umgeleitete Message die Nummer MsgNr% hat und für das Fenster/Dialogelement mit dem Handle hWnd% bestimmt ist. Diese Funktion vereinfacht also die folgende Abfrage:

n% = (%sMessage = MsgNr%) and (&sWnd = &hWnd)

Zusätzlich setzt diese Funktion im Erfolgsdfall, also Message und Fensterhandle passen, WinProc auf 0, da diese Message ja nun von uns behandelt wird. Sollte die Message trotzdem auch noch von Windows behandelt werden, so können wir mit Set("WinProc", 1) dieses auch erreichen.

WICHTIG: In der SubClassProc sollte so wenig wie möglich geschehen. In den allermeisten Fällen wird man mit SetMenuItem einen Befehlscode setzen. Sobald dieser gesetzt ist, wird das WaitInput verlassen und der Befehlscode kann mit MenuItem() oder %MenuItem abgefragt werden.

HINWEISE:

1. Die SubClassProc wird nur dann aufgerufen, während sich das Programm im WaitInput befindet.

2. Die SubClassProc wird defaultmäßig im FastMode aufgerufen. Damit soll verhindert werden, dass bei der Verarbeitung der umgeleiteten Messages weitere Messages erzeugt werden. Das führt nämlich sehr rasch zu einer Endlosschleife. Wer in der Bedienung des Taskmanagers bewandert ist, kann natürlich gerne auch mal ausprobieren den FastMode mit Set("FastMode",0) in der SubClassProc auszuschalten.

Der Vollständigkeit halber:

n% = Get("WinProc")

Hiermit ermitteln wir der aktuellen Wert von WinProc (entweder 0 oder 1)

Hier ein einfaches Beispiel:

$H Messages.ph

$H Windows.ph

SubClassProc

  If SubClassMessage(%hWnd, ~wm_sizing)

    SetStyle %hwnd, 1, GetStyle(%hwnd, 1) | $02000000

    Resize

  ElseIf SubClassMessage(bt&, ~wm_rbuttondown)

    SetText bt&, "Autsch!"

  ElseIf SubClassMessage(bt&, ~wm_rbuttonup)

    SetText bt&, "Test1"

    SetMenuItem 3000

  ElseIf SubClassMessage(st&, ~wm_mousemove)

    SetMenuItem 3001

  ElseIf SubClassMessage(%hwnd, ~wm_close)

    SetMenuItem 3999

  EndIf

EndProc

Proc Resize

  SetWindowPos bt& = 0, 50 - Width(%HWnd)/2, Height(%HWnd) - 75

  SetWindowPos bt2& = Width(%HWnd)/2, 50 - Width(%HWnd)/2, Height(%HWnd) - 75

  SetWindowPos st& = 0, 0 - 0,0; 0

  SetWindowPos tb& = 0, 0 - 0,0; 0

EndProc

declare bt&, bt2&, st&, tb&

declare ende%

cls

st& = create("StatusWindow", %HWnd, "Statuszeile")

tb& = create("Toolbar", %HWnd,0,15,1,1000,1)

bt& = create("Button", %HWnd, "Test1", 0, 50, Width(%HWnd)/2, Height(%HWnd) - 75)

bt2& = create("Button", %HWnd, "Test2", Width(%HWnd)/2, 50, Width(%HWnd)/2, Height(%HWnd) - 75)

SubClass %HWnd, 1 'SubClassing des Hauptfensters einschalten

SubClass bt&,   1 'SubClassing des 1. Buttons einschalten

SubClass st&,   1 'SubClassing der Statuszeile einschalten

whilenot ende%

  waitinput

  If %Key = 4

    Resize

    SetStyle %hwnd, 1, GetStyle(%hwnd, 1) - $02000000

  ElseIf MenuItem(3000)

    MessageBox("Rechtsklick auf Button 1!","Test",0)

  ElseIf MenuItem(3001)

    MessageBox("Mausbewegung über Statuszeile!","Test",0)

  ElseIf MenuItem(3002)

    MessageBox("wm_activate","Test",0)

  ElseIf MenuItem(3003)

    MessageBox("wm_syscommand",str$(&sWParam),0)

  ElseIf MenuItem(3999)

    Case Messagebox("Wollen Sie das Programm wirklich verlassen?","Frage",36) = 6 : Ende% = 1

  EndIf

endwhile

' Subclassing wieder ausschalten

SubClass %HWnd, 0

SubClass bt&, 0

SubClass st&, 0

end


Erweiterte Befehle und Funktionen

SizeOf(VAR)

Wenn VAR ein gültiges Bitmap- oder Metafilehandle ist, werden die Systemvariablen %BmpX, %XBmpY, %BmpCol und %BmpB beziehungsweise %MfX und %MfY entsprechend gefüllt.

RePaint [modus%]

Im Modus 0 wird wie bisher der Bildschirm vor dem Neuzeichnen gelöscht. Im Modus 1 wird nur der veränderte Teil neugezeicnet. Wird Modus nicht angegeben, funktioniert RePaint wie bisher.

WinHelp für CHM-Dateien

Der Befehl Winhelp kommt nun auch mit CHM-Dateien klar. Wenn die Dateiendung nicht ".hlp" ist, wird angenommen, dass es sich um eine CHM-Datei (Dateiendung .chm) handelt. Die Sonderfunktionen von "*" und "?" als Suchbegriff gibt es bei CHM-Dateien nicht.

ClassOf() wurde erweitert, um Handles und Devicekontexte zu identifizieren

Bislang ermittelt ClassOf die Klassen von Objekten und die Fensterklassen von Windowsfenstern- und Dialogelementen. Oftmals ist es auch wichtig, zu ermitteln, ob ein Handle einem Icon oder einer Bitmap gehört. Folgende Handleklassen werden identifiziert:

- ImageList

- BitMap

- MetaFile

- MetaDC

- DC

- Icon (auch bei einem Cursor-Handle, da Icon und Cursor technisch das Gleiche sind)

- Module (Handle einer mit UseDLL() oder ImportDLL() geladenen Datei, kann auch eine EXE sein)

SetText und GetText$() nun auch für TreeView und TabControl

Für das neue TreeView-Control wurden SetText und GetText$() erweitert:

SetText hTreeView&, hItem&, Text$

Text$ = GetText$(hTreeView&, hItem&)

Auch beim TabControl ist es nun möglich, den Text eines Reiters zu verändern und abufragen:

SetText hTabCtrl&, TabNr%, Text$

Text$ = GetText$(hTabCtrl&, TabNr%)

Eine ImageList für die ToolBar

Bei Create("Toolbar",...) kann jetzt anstelle des Bitmaphandles für die Icons auch eine Imagelist angegeben werden:

hToolbar& = Create("Toolbar", hWnd&, hImageList&, Anzahl%, 0, BefehlsNr%, Mode%)

Der Parameter für die Größe der Icons wird nicht verwandt und kann auf 0 gesetzt werden. Es wird die Größe der Icons in der ImageList verwandt. So sind transparente Toolbar-Icons in beliebiger rechteckiger Größe kein Problem mehr.

Eine ImageList für das TabControl

Bei Create("TabCtrl",...) kann jetzt anstelle des Textes für den ersten Reiter eine ImageList angegeben werden. Das TabControll wird dann zunächst ohne Reiter, aber mit ImageList erzeugt:

hTabCtrl& = Create("TabCtrl", hWnd&, hImageList&, X%, Y%, DX%, DY%)

Die Reiter werden wie gewohnt mit InsertTab() hinzugefügt, aber damit man die ImageList auch nutzen kann, gibt es bei InsertTab einen optionahlen zusätzlichen Parameter für die Nummer des Bildes aus der ImageList:

InsertTab(hTabCtrl&, Nr%, Text$, BildNr%)

Bef% = TrackMenu([x%,y%])

Anstelle des Befehles TrackMenu x%,y% kann nun auch die gleichnamige Funktion verwandt werden. Das Ergebnis ist der gewählte Menüpunkt. Wird die Funktion ohne die Parameter x%,y% mit leerer Klammer aufgerufen, wird die aktuelle Mausposition für das Popup-Menü verwandt.

DeleteString(0) und GetCount(0) für Listbox-Liste

Wenn bei DeleteString als Handle die 0 angegeben wird, bezieht sich die Funktion auf die Listbos-Liste. Somit können jetzt auch aus der Listbox-Liste gezielt Einträge entfernt werden. Das Ergebnis der Funktion ist auch hier die Anzahl der in der Liste verbleibenden Elemente bzw. -1 wenn das Element nicht gelöscht werden konnte.

Ebenso bezieht sich GetCount() auf die Listbox-Liste, wenn das Handle 0 ist, und gibt die Anzahl der Elemente in der Listbox-Liste wieder.

SetText für DateEdit und TimeEdit

Bei "DateEdit"- und "TimeEdit"-Controls kann nun mit SetText der eingestellte Wert geändert werden. Wichtig ist das korrekte Format. Hier gelten die gleichen Regeln wie für den Anfangswert bei der entsprechenden Create-Funktion.

SetWindowsPos mit nur zwei Parametern

SetWindowPos ist jetzt auch in folgender Form möglich:

SetWindowPos hWnd&, x%, y%

Das Fenster oder Dialogelement mit dem Handle hWnd& wird ohne die Größe zu verändern an die neue Position verschoben.

Dispose für mehrere Beeiche

Bei Dispose können nun mehrere Bereichs-, Objekt- oder Strukturvariablen

angegeben werden, deren Speicher freizugeben ist:

Dispose Ber1#, Ber2#, Ber3#

Clicked() verbessert

Clicked() reagiert jetzt auch auf einige Dialogelemnte, auf die es bisher nicht reagiert hat, wie z.B. auf die Gridbox.

Inc und Dec erweitert / Add und Sub abgeschafft

Die Befehle INC und DEC können jetzt einen weiteren Parameter haben:

Inc Var%[, N%]

Dec Var%[, N%]

Die Variable Var% wird bei Angabe des optionalen Integer-Parameters N% um den entsprechenden Wert erhöht oder erniedrigt. Damit werden die bisherigen Befehle ADD und SUB überflüssig. Aus Kompatibilitätsgründen werden sie aber vom Interpreter bzw. Compiler automatisch in die neue Form übersetzt.

Substr$ erweitert

Wenn bei Substr$ kein Delimiter angegeben wird, wird statt des Substrings ein einzelnes Zeichen ermittelt:

SubStr$(Test$,  n%) ermittelt das n. Zeichen von Test$

SubStr$(Test$, -n%) ermittelt das n.-letzte Zeichen von Test$

Erweiterte Usermessages

Um eine einzelne UserMessage wieder zu entfernen, ist die Messagenummer beim Befel USERMESSAGES negativ einzugeben. Beispiel:

UserMessages -16

Die Message 16 wird aus der Liste der Usermessages entfernt. Alle anderen

eingestellten Usermessages bleiben davon unberührt.

&loop in anderen Schleifen bekannt

Bislang war &loop undefiniert, wenn es innerhalb einer WhileLoop-Schleife weitere While- oder WhileNot-Schleifen gab. Ab der kommenden Version ist in diesen das letzte &loop bekannt. Bei verschachtelten WhileLoop-Schleifen hat in der Schleifenbedingung einer WhileLoop-Schleife %Loop noch den Wert der äußeren WhileLoop-Schleife.

Menüeigenschaften auch bei PopUp-Menüs mit TrackMenu

Bislang konnte ein TrackMenu nicht mit CheckMenu, EnableMenu, InsertMenu und RemoveMenu verändert werden. Das ist nun zwischen dem Erstellen des Menüs mit CreateMenu und der Anzeige mit TrackMenu möglich. Ebenso hat &hMenu dann den entsprechenden Wert. Nach Trackmenu, hat &hMenu wieder den Wert des Hauptmenüs (oder 0, wenn es keines gibt).

SetText kann nun auch den Tooltiptext ändern und mit einer Überschrift samt Icon versehen:

SetText hToolTip&, Fenster&, Element&, Text$

Der entsprechende Tooltip erhält einen neuen Text.

SetText hToolTip&, Fenster&, Element&, Text$, Icon%, Titel$

Der entsprechende Tooltip erhält einen neuen Text und die Überschrift Titel$ mit dem gewählten Icon. Die Werte für Icon können zwischen 0 und 3 liegen und stellen die üblichen Systemicons dar. Ab Windows XP darf Icon% auch ein beliebiges Incon-Handle sein.

create("hIcon", Name$)

Wenn diese Funktion nur einen Parameter hat, dann bezeichnet dieser den Namen eines internen XProfan-Icons.


Sonstige Erweiterungen

SetEnv Name$, Wert$

Name$: Name der Environmentvariablen

Wert$: Neuer Wert der Environmentvariablen

Die Environmentvariable mit dem entsprechenden Namen wird auf Wert$ gesetzt, bzw. mit Wert$ erzeugt, wenn sie noch nicht existiert.

Farbe% = ChooseColor(hWnd&, Vorgabe%)

hWnd&: Übergeordnetes Fenster

Vorgabe%: Vorgabefarbe

Ergebnis: Die ausgewählte Farbe oder -1 bei Abbruch

Es wird der Windows-Farbwahldialog aufgerufen. Mit Vorgabe& wird die Farbe bestimmt, die bei Aufruf des Dialoges ausgewählt ist. In der Regel wird es die Farbe sein, die zum Ändern angeboten wird. Der REückgabewert ist -1, wenn der Dialog mit Abbruch beendet wird oder der gewählte Farbwert. Das übergeordnete Fenster ist wichtig für die Positionierung des Dialoges.

Error ErrNr%, ErrText$

Dieser Befehl erlaub die Erzeugung einer Fehlermeldung. Hierbei kann ErrNr% eine XProfan-Fehlernummer sein oder ein benutzerdefinierter Wert von 1 bis 99. ErrText$ ist die Fehlermeldung. Bei XProfan-Fehlernummern wird ErrText$ an den Text der Fehlermeldung angehängt. Ist ErrNr% weder im Bereich 1 bis 99, noch eine gültige XProfan-Fehlermeldung, dann wird der Fehler "UNERWARTETER FEHLER!" ausgegeben. Diese selbst definierten Fehler können wie "echte" XProfanfehler mit Set("ErrorLevel", Level%) unterdrückt werden und/oder mit ErrorProc abgefangen werden.

HTML-Download

Eine neue Funktion erlaubt das Laden von Dateien per HTML. Das können beliebige Dateien sein, also z.B. HTML-Dateien oder auch ZIP-Archive:

ID% = DownLoadFile(Url$[, Datei$])

Url$: Web-Adresse der Datei

Datei$: Dateiname (auch mit Pfad), unter dem die Datei gespeichert werden soll

Ergebnis: Ist ID% > 0 wurde der Download erfolgreich gestartet

Der Download findet in einem eigenen Thread statt, das heißt: das Programm läuft weiter, während der Download erfolgt. Folgende Systemvariablen geben Auskunft über den Zustand bzw. das Ergebnis des Downloads:

%Loading: 1 = Download ist noch nicht fertig; 0 = Download ist fertig bzw. zur Zeit kein Download

&BytesRead: Die aktuell gelesene Anzahl an Bytes. -1 bedeutet, dass beim Download ein Fehler aufgetreten ist.

$Download: Wird kein Dateiname zum speichern angegeben, enthält $Download den Inhalt der geladenen Datei, ansonsten "0" im Erfolgsfall.

Hier wird eine HTML-datei angezeigt:

cls

var url$ = "http://xprofan.de/regeln.htm"

downloadfile(url$)

while %Loading

endwhile

print $Download

waitinput

end

Ein Beispiel für das Downloaden einer Datei:

var url$ = "http://xprofan.de/download/prbase32.zip"

downloadfile(url$, "c:/test.zip")

while %Loading

   locate 1,1

   print &bytesread;" Bytes geladen    "

endwhile

print "Fertig!"

waitinput

end

Beschleunigtes Explode(), MoveStrToList(), InStr(), SubStr$()

Bei einem mehrere MB großen String mit mehreren hunderttausend Substrings konnte es bislang einige Minuten dauern, bis Explode() oder MoveStrToList() mit ihrer Arbeit fertig waren. Dieser Vorgang konnte auf unter 1 Sekunde beschleunigt werden! In diesem Zusammenhang wurde auch Substr$ und insbesondere Instr() mit drittem Parameter beschleunigt.

Weitere Unterfunktionen bei Toolbar()

Die Funktion Toolbar() hat einige weitere Unterfunktionen:

Toolbar("ShowButton", hTB&, ID%, n%):   Der Button ID% wird bei n% = 0 unsichtbar gemacht und mit n% = 1 wieder sichtbar

Toolbar("EnableButton", hTB&, ID%, n%): Der Button ID% wird bei n% = 0 deaktiviert und mit n% = 1 wieder aktiviert

Toolbar("CheckButton", hTB&, ID%, n%):  Der Button ID% wird bei n% = 1 "eingedrückt" und verharrt in dieser Stellung. Bei n% = 0 wird er wieder gelöst.

Toolbar("IsChecked", hTB&, ID%):        Diese Funktion überprüft ob der entsprechende Button gerade eingedrückt ist oder nicht. Ist er eingedrückt, ist das Ergebnis 1, ansonsten 0.

UserMessage-Stack

Bisher konnte es passieren, dass Usermessages verloren gingen, wenn mehrere Usermessages zwischen zwei WaitInputs auftraten. In diesem Fall kam nur die letzte zum Zuge. Jetzt gibt es einen Stack, der sich die aufgetretenen Usermessages merkt und sie in der Reihenfolge des Eintreffens an WaitInput weitergibt. An der Syntax ändert sich dadurch nichts.

Erweiterte Usermessages

Um eine einzelne UserMessage wieder zu entfernen, ist die Messagenummer beim Befel USERMESSAGES negativ einzugeben. Beispiel:

UserMessages -16

Die Message 16 wird aus der Liste der Usermessages entfernt. Alle anderen eingestellten Usermessages bleiben davon unberührt.

Mit der Funktion IsUsermessage(Nr%) kann überprüft werden, ob es die UserMessage Nr% gibt. In diesem Fall ist das Ergebnis 1, ansonsten 0.

Bislang liefern die UserMessages als Ergebnis ihre Messagenummer zurück. Das ist defaultmäßig weiterhin so. Jedoch kann mit einer neuen Funktion der Antwortwert geändert werden:

SetUAnswer(16, 32)

Der UserMessage 16 wird der Antwortwert 32 zugeteilt. Im Erfolgsfall ist das Ergebnis der Funktion 1, ansonsten 0. Ist die MessageNummer keine UserMessage, erfolgt eine Fehlermeldung.

Base64-Codierung

In vielen Fällen, z.B. beim Versenden von Mailanhängen, ist es nötig, beliebige Daten in einen String aus darstellbaren Zeichen zu verwandeln. Der bekannteste Code hierfür ist Base64, d.h. alle Werte werden in maximal 64 Zeichen dargestellt:

A - Z, a - z, 0 - 9 und + sowie /. Als Endekennzeichen dient das =.

Folgende Funktionen ermöglichen die Kodierung und Dekodierung von beliebigen Inhalten:

text$ = Encode64(data$)

data$ = Decode64(text$)

Der String data$ kann beliebe Zeichen von Code 0 bis 255, also auch nicht darstellbare Zeichen, enthalten. Somit lassen sich beliebe Daten und Dateien in Strings verwandeln und umgekehrt.

XProfan benutzt die selbe Codierung, wie sie z.B. auch für den eMail-Verkehr Standard ist.

SendFile bei SMTP

smtp("SendFile",...) hat die gleiche Syntax wie "SendMail", nur dass zusätzlich noch ein Dateiname angegeben werden muß. Diese Datei wird als Anhang mitgeschickt:

   declare datei$

   cls

   datei$ = loadfile$("Datei laden:","*.jpg")

   print smtp("SendFile", "rgh-soft@t-online.de", "roland@huelsmann.net", \

                          "Neue SendFile-Funktion", \

                          "popmail.t-online.de", "*********", "********", \

                          "Das ist der Text der Mail! \nGruß Roland", datei$)

   waitinput

   end

Default-Position des Hauptfensters zentriert

Bislang wurde das Programmfenster immer oben links auf dem Bildschirm angezeigt, wenn es durch CLS oder den ersten PRINT-Befehl erzeugt wird. Bei immer größeren Bildschirmen wirkt das zunehmend unschön, zumal mancher dort seine wiochtigsten Icons plaziert hat, die er nicht gerne überdeckt sieht. Ab XProfan 11 öffnet sich das Programmfenster in diesen Fällen zentriert in der Mitte des Bildschirmes. Aus Kompatibilitätsgründen bleibt die Größe des Fensters aber unverändert.

nearly(x!, y!, n&)

x!: Double

y!: Double

n&: Genauigkeit

Ergebnis: 0 oder 1

Naturgemäß ist der exakte Vergleich von Fließkommazahlen in manchen Fällen problematisch, insbesondere dann, wenn zum Vergleich nicht alle möglichen Stellen berücksichtigt werden sollen.

Als Lösung bietet sich die neue Funktion an, wobei Genauigkeit die gleiche Bedeutung wie bei round(wert!, genauigkeit%) hat, und die Anzahl der Nachkommastellen angibt, die beim Vergleich wirken. Diese Zahl kann auch negativ sein, um etwa bei großen Zahlen die hinteren Stellen nicht zu berücksichtigen. nearly(6.119, 6.124, 2) würde dann 1 (=wahr) ergeben, da beide Werte auf 2 Stellen gerundet gleich sind, nämlich 6.12.

Menüeigenschaften auch bei PopUp-Menüs mit TrackMenu

Bislang konnte ein TrackMenu nicht mit CheckMenu, EnableMenu, InsertMenu und RemoveMenu verändert werden. Das ist nun zwischen dem Erstellen des Menüs mit CreateMenu und der Anzeige mit TrackMenu möglich. Ebenso hat &hMenu den entsprechenden Wert.

set("OGLW2H", wert!)

wert! = get("OGLW2H")

Normalerweise wird das Seitenverhältnis des OpenGL-Bildschirmes automatisch bestimmt, so dass ein Kreis unabhängig von der Größe des OpenGL-Fensters immer ein Kreis und ein Quadrat immer ein Quadrat ist, und das Bild nicht verzerrt wird. Das führt unter Umständen bei bestimmten Fenstergrößen zu unbenutzten Rändern. Möchte man jedoch erreichen, daß der Bildinhalt entsprechend der Fenstergröße verzerrt wird, kann man mit dieser SET-Funktion das Seitenverhältnis fest vorgeben, so dass die automatische Anpassung unterbleibt. Ein Beispielcode:

' Programm: lesson3 enhanced

' Open GL für XProfan 10

proc DrawGLScene

   oGL("Clear")

   oGL("Origin", -1.5, 0, -6.0)

   oGL("Color", 0.0, 0.0, 1.0, 1.0)

   oGL("Triangle", 2, 2)

   oGL("Origin", 1.5, 0, -6.0)

   oGL("Color", 1.0, 0.0, 0.0, 1.0)

   oGL("Quad", 2, 2)

   oGL("Show")

endproc

' Hauptprogramm

' -------------

declare ende%

CLS 0

' Zum Vergleich die folgende Zeile

' auskommentieren

Set("OGLW2H", 4/3)

oGL("Init", %hWnd, 0, 0, 0, 1)

oGL("PosMode", 1)

DrawGLScene()

ende% = 0

WhileNot ende%

   WaitInput

   if %key = 27

     ende% = 1

   elseif %key = 4

     DrawGLScene()

   endif

EndWhile

end


Anhang 1: Behobene Bugs

Bei im offiziellen Forum gemeldeten Bugs verweise ich auf die Bugmeldung.

Fehler bei verschachtelten DEF-Funktionen mit mehreren Parametern

Der Bug muß schon immer vorhanden gewesen sein. wurde jedoch erst jetzt (S8) bemerkt und gefixt: Wenn eine mit DEF definierte Funktion mit mehreren Parametern eine weitere mit DEF definierte Funktion mit ebenfalls mehreren Parametern aufrief, stimmte das Ergebnis nicht.

Fehler beim Compilieren mit Variablenoptimierung

Bislang wurde im fertigen Programm die Fehlermeldung "doppelter Variablenname" produziert, wenn von einem Variablentyp mehr als 52 Exemplare vorkamen. Der Fehler wurde behoben.

Außerdem kam es zur Fehlermeldung "Variable nicht gefunden", wenn die eingebaute Struktur oGL-Sprite verwandt wurde. Dieser Fehler wurde behoben, indem die Namen von Struktur- und Objekteigenschaften nicht optimiert werden.

Input$ verschluckt Leerzeichen

http://www.rgh-soft.de/forum01/read.php?f=9&i=3097&t=3097

Abstürze in oGL-Programm

http://www.rgh-soft.de/forum01/read.php?f=9&i=3076&t=3076

Bug beim Vererben von Strukturen

http://www.rgh-soft.de/forum01/read.php?f=7&i=12582&t=12538

Escape-Zeichen bei MCISend$()

http://www.rgh-soft.de/forum01/read.php?f=6&i=4579&t=4577


Anhang 2: Mögliche Kompatibilitätsprobleme

Operator-Ersatz-Funktionen nicht mehr direkt unterstützt

Wie schon lange angekündigt, wurden die Operator-Ersatz-Funktionen aus der XProfan-Syntax herausgenommen.

Folgende Funktionen gibt es daher nicht mehr:

Add(), Sub(), Div(), Div&(), Mul(), Mod(), Pow()

And(), Or(), Equ(), Neq(), Gt(), LT()

Add$(), Equ$(), Neq$(), Gt$(), LT$()

Die entsprechenden Operatoren gibt es seit Profan² 6.0.

Damit aber auch noch Programme interpretiert und kompiliert werden können, die auf noch älteren Quellcodes beruhen, wird eine Include-Datei PROFALT.INC mitgeliefert, die diese Funktionen enthält.