Das folgende Listing zeigt, wie man eine beliebige ASCII-Text-Datei lädt und diese in einem einfachen GEM-Fenster vertikal scrollt. Die routine lässt sich in beliebige Programme einbinden und bietet besonders bei Programminfos einen besonderen Reiz, da man hier ja mit Showeffekten glänzen darf.
Zuerst werden einige Initialisierungen durchgeführt: Die maximal zulässige Zeilenanzahl ist durch die Anzahl der Elemente des Stringfelds Zl$() begrenzt, hier wurde 1000 gewählt, was für die meisten ASCII-Texte ausreichen dürfte. Man könnte zur variablen Felddimensionierung natürlich auch die Häufigkeit des ASCII-Zeichens Nr. 13 (=Carriage return) feststellen, das zusammen mit dem ASCII-Zeichen Nr. 10(=Line feed) ein Zeilenende markiert. Dessen Anzahl entspräche dann der Zeilenanzahl der ASCII-Datei. Mit diesem Wert könnte dann das Stringfeld variabel dimensioniert werden. Das Feld wird zunächst mit 74 Leerzeichen pro Feldelement gefüllt, soviele Zeichen pro Zeile haben im Fenster Platz. In diese ‘Leerzeilen' werden die Zeilen der ASCII-Datei später linksbündig eingepaßt. Die folgenden Programmzeilen dienen der Fensterverwaltung, der Übersichtlichkeit wegen bekommen die benutzten Window-Messages Namen. Nach der Auswahl einer ASCII-Datei wird diese zeilenweise in das Feld Zl$() geladen. Für das Scrollen ist es wichtig, daß jede Zeile die max. Länge von 74 Zeichen hat, sonst wird die alte Zeile nicht komplett überschrieben und rechts bleiben Textreste stehen. Aus diesem Grund werden die Zeilen mit dem Befehl LSET linksbündig in das Stringfeld eingepaßt. Mit Wind_Create(...) werden das Aussehen und die max. Größe des Fensters festgelegt. Der Wert '211' = binär 11010011 ist eine Bitmaske. Durch das Setzen der einzelnen Bits werden die entsprechenden Bedienungselemente des Fensters eingeschaltet. Es sind maximal 12 Bits möglich:
Titelzeile %1
Closer %10
Fuller %100
Mover %1000
Infozeile %10000
Sizer %100000
Uparrow %1000000
Dnarrow %10000000
Vslide %100000000
Lfarrow %1000000000
Rtarrow %10000000000
Hslide %100000000000
Das hier benutzte Fenster besteht nur aus Titelzeile, Infozeile. Uparrow. Dnarrow und Closer. In den letzen Parameter schreibt GEM das sogenannte Window-Handle. Das ist die Kennziffer des Fensters, damit beim Arbeiten mit mehreren Fenstern jedes einzelne angesprochen werden kann. Das Desktop erhält das Handle 0. alle weiteren Fenster die Handles 1-7. Sollte kein Fenster mehr verfügbar sein (AES verwaltet bis zu 8 Fenster), wird der Wert-1 zurückgeliefert.
Die Prozedur Show_Window setzt Titel- und Infozeile (Wind_Set(...)). das Fenster wird mit Wind_Open(...) geöffnet und der Fensterinhalt gelöscht. Jetzt wird der Textanfang im Fenster ausgegeben und die Kontrolle an das AES übergeben. In der ‘REPEAT... UNTIL'-Schleife werden die Fensterelemente abgefragt. Wurde Uparrow oder Dnarrow angeklickt, wird zu den Scrollroutinen verzweigt, wurde der Closer angeklickt, wird das Fenster geschlossen. Jetzt kommt der Kern der Sache, die Scroll-Routinen. Im Fenster werden 20 Textzeilen dargestellt. Um nun z.B. nach oben zu scrollen, müssen die oberen 19 Zeilen nach unten geschoben und die neue Zeile 1 ausgegeben werden. Die Ausgabe der neuen Zeile 1 sollte mit einer einfachen Textausgabe wohl keine Probleme bereiten. Und das Verschieben der oberen 19 Zeilen erledigt man mit dem fixen BITBLT-Befehl. -Denkste ! - Der Text schlingert in wellenförmigen Bewegungen (“An der Nordseeküste...") derart lustlos über den Bildschirm, daß man nur mit Mühe die aufkommende Übelkeit unterdrücken kann. Erst wenn man den Blitter einschaltet (hat aber nicht jeder), kann man damit leben. Also begab ich mich auf die Suche nach einer schnelleren Möglichkeit, den Bildschirm durch die Gegend zu schieben und fand diese in Gestalt des (übrigens nicht im Handbuch dokumentierten) Befehls MEMORY_MOVE (B). Mit diesem Befehl kann man recht schnell Speicherbereiche kopieren; eigentlich müßte er MEMORY_COPY heißen, da der Speicherbereich ab der Quelladresse ja nicht gelöscht wird. Der Befehl MEMORY_MOVE <Quelle>, <Anzahl> TO <Ziel> verschiebt eine Anzahl Bytes von der Quell- zur Zieladresse. Da dieser Befehl intern wortweise arbeitet, müssen alle drei Werte gerade Zahlen sein. Falls dies nicht möglich ist. gibt's den Befehl MEMORY_MOVEB <Quelle>, <Anzahl> TO <Ziel>, der intern byteweise arbeitet, dadurch auch aber erheblich langsamer ist. Der Aufbau des Bildschirmspeichers wurde schon öfters in verschiedenen Artikeln und Büchern erklärt, deshalb setze ich ihn als bekannt voraus.
Da am linken Rand ein Stück des Hintergrundes und des Fensterrahmens zu sehen ist, die natürlich nicht verschoben werden sollen, fängt der Scrollbereich ab dem 2. Datenwort einer Pixelzeile an. Die Zeile ist 37 Wörter = 74 Bytes lang, und die rechte Grenze liegt beim 38. Wort. Dieser Bereich wird nun 16 Pixelzeilen (= 1280 Bytes) nach unten kopiert: nachdem man 16 Pi-xel-Zl. kopiert hat, ist 1 Textzeile verschoben worden. Doch da tritt ein ziemlich fieser Denkfehler auf; Die 1. Textzeile wird in die 2. Textzeile kopiert, die 2. Textzeile wird in die 3. - HALT! In der vorher 2. Textzeile steht ja jetzt die 1. Textzeile! Ergebnis des ersten Scrollversuchs: Auf dem Bildschirm steht 20mal die 1. Textzeile. Der Fehler ist umso gemeiner, da er beim Verschieben in die andere Richtung nicht auftritt. Um diesen Fehler zu verhindern, muß der Schleifenzähler gegen die Verschieberichtung laufen, d.h. beim Verschieben nach unten muß man in der letzten Zeile anfangen und sich nach ‘oben arbeiten', beim Verschieben nach oben läuft der Schleifenzähler wie gewohnt von oben nach unten. Mit MEMORY_MOVE ist das Scrolling etwas schneller als mit BITBLT, doch richtig ab geht's erst, wenn das Programm compiliert wird: das Scrolling erreicht dann etwa die 5fache Geschwindigkeit. Noch etwas schneller geht's in Assembler: ersetzt man die FOR...NEXT-Schleife in den Scrollroutinen durch den INLINE-Befehl, so kann man auch im Interpreter schnell scrollen. Damit der Compiler die INLINE-Strings verdaut, darf man nur die Register d1-d5 und a0-a2 benutzen. Außerdem darf am Ende der INLINE-Routine kein RTS stehen. wie es beim Aufruf mit CALL notwendig ist (scheinbar erledigt INLINE das selbst). REPEAT...UNTIL MOUSEBUT=0 sorgt dafür, daß bei festgehaltener linker Maustaste der Text immer weiter scrollt: da das Betriebssystem bei up_arrow und dn_arrow keine festgehaltene Maustaste bemerkt. Erst ab der TOS-Version 1.4 funktioniert das, dort kann die Schleife weggelassen werden. Nachdem der Text um eine Zeile gescrollt worden ist, wird die Variable Topline um 1 erhöht/erniedrigt, da ja jetzt eine andere Textzeile in der 1. Zeile steht. Die Nr. der neuen Zeile 1 wird mit TEXT... direkt in die Infozeile des Fensters geschrieben. Wer jetzt schreit, daß das keine saubere GEM-Programmierung ist. der kann ja ‘mal die Infozeile mit WIND_SET(...) aktualisieren (ei, wie das flackert und lange dauert...).
Noch ein paar Bemerkungen zur Ausgabe mit TEXT: Solange man die Ausgabe horizontal auf Bytegrenzen legt. d.h. die x-Ordinate ohne Rest durch 8 teilbar ist, und auf Textattribute (kursiv, fett, hell...) verzichtet, ist der Befehl recht schnell. Wer’s nicht glaubt, kann ja mal die x-Ordinate von 16 auf 17 setzen und ein beliebiges Attribut mit TEXT STYLE setzen (Kaffeemaschine einschalten wird empfohlen). Tja und dann muß an dieses Listing natürlich noch die GEMLIB angehängt werden. Es werden jedoch nur einige der GEM-Prozeduren benötigt. Da sich die GEM-Prozeduren teilweise gegenseitig aufrufen, ist das Aussortieren ziemlich nervtötend (eigentlich soll das Programm GEMSEL.BAS auf der Omikron.BASIC-Diskette das können, aber bei mir hat es bisher versagt). Nur die folgenden Prozeduren/Funktionen werden benötigt, wobei die Zahlen dahinter die Anzahl der Übergabeparameter angeben (einige Prozeduren mit gleichen Namen unterscheiden sich nur durch die Anzahl der Parameter; keine besonders elegante Lösung...).
So, das war’s zum Thema Text-Scrolling in Omikron. BASIC. Wer unbedingt mehr als 74 Zeichen pro Zeile verarbeiten möchte, kann ja eine Routine für horizontales Scrolling mit Lfarrow und Rtarrow programmieren.
Appl_Init, Appl_Exit, Evnt_Mesag(l), Menu_Bar, Form_Dial(5), Form_Dial(9), Graf_Handle(5), Graf_ Mouse(l), Wind_Create(6), Wind_Open (5), Wind_Close(l), Wind_Delete(1), Wind_Get(6), Wind_Set(4), Wind_Update(1), Rsrc_Free. Vst_ Height(5), V_Clswk, V_Clsvwk, Vs_Ndc, Patch_Basic(4).
Benötigte Funktionen:
Wind_Check(2), Rsrc_Check(1), V_Opn_Check(1), V_Opnv_Check(1), Reserved(1).
'***********************************************
'* Text-Scrolling im GEM-Fenster *
'* Autor: Andreas Hollmann, Paderborn *
'* (c) MAXON Computer GmbH *
'* Sprache: Omikron.BASIC *
'***********************************************
DEFINTL "A-Z"
Prg_Init ' einige Initialisierungen durchführen
F_Select(Load$,Button%L)' ASCII-Text auswählen
IF Button%L=1 THEN ' 'OK' Button gewählt
Load_Ascii(Load$,F_Len%L)'ASCII-Datei laden
Appl_Init' GEM-Anwendung anmelden
MOUSEOFF
Wind_Create(211,0,0,640,400,W1%L)' Fenster entwerfen
F_Len$= SPACE$(22)+"Länge: "+ STR$(F_Len%L)+" Bytes"
Wf_Name$=" "+Load$+" "' Inhalt Titelzeile
Wf_Info$=" Zeile: 1"+F_Len$' Inhalt Infozeile
Show_Window(W1%L,630,370,Wf_Name$,Wf_Info$)
Show_Text(Topline%L)' Textanfang ausgeben
MOUSEON
Watch_Mouse(W1%L)' jetzt kann gescrollt werden
Appl_Exit' GEM-Anwendung abmelden
ENDIF
PRINT ""' Cursor ausschalten
END ' Programm beenden
'========================================================
DEF PROC Prg_Init
PRINT ""' Cursor ausschalten
XBIOS (Scr%L,2)' Bildschirm-Adresse in Scr
DIM Zl$ (1000) ' Platz für 1000 Zeilen reservieren
FOR Offset%L=0 TO 999' Feld mit Leerzeichen füllen
Zl$(Offset%L)= SPACE$(74)
NEXT Offset%L
DIM Msg%L(10)' Platz für Message-Puffer reservieren
Name_Mem%L= MEMORY(128)' Platz für Titelzeile und
Info_Mem%L= Name_Mem%L+64' Infozeile reservieren
Wm_Closed%L=22' Window-Messages benennen...
Wm_Arrowed%L=24
Wa_Upline%L=2
Wa_Dnline%L=3
Wf_Name%L=2
Wf_Info%L=3
Wf_Workxywh%L=4
RETURN
'--------------------------------------------------------
DEF PROC F_Select(R Path_File$,R Button%L)
LOCAL Drive%L,Drive$,Path$,F_Name$,Cut%L
GEMDOS (Drive%L,25)' aktuelles Laufwerk ermitteln
Path$= CHR$(65+Drive%L)+":"' Pfad = aktuelles Laufwerk
MOUSEON
FILESELECT (Path$,F_Name$,Button%L)
MOUSEOFF
Cut%L= INSTR( MIRROR$(Path$),".")+1' *.EXT abschneiden
Path_File$= LEFT$(Path$,( LEN (Path$)-Cut%L))+F_Name$
RETURN
'--------------------------------------------------------
DEF PROC Load_Ascii(Load$,R F_Len%L)'ASCII-Datei laden
LOCAL Buffer$
CLS
TEXT 240,192,"Datei wird geladen..."
OPEN "i",1,Load$
F_Len%L= LOF(1)
Lines%L=1
WHILE NOT EOF(1)
LINE INPUT #1,Buffer$' 1 Zeile in Puffer laden
LSET Zl$(Lines%L)=Buffer$' auf 74 Zeichen bringen
Lines%L=Lines%L+1' Zeilen-Anzahl um 1 erhöhen
WEND
CLOSE 1
CLS
RETURN
'--------------------------------------------------------
DEF PROC Watch_Mouse(W_Handle%L)' auf Maus-Klick warten
LOCAL I%L,Message$,Msg%L
REPEAT ' solange in der Schleife bleiben,...
Evnt_Mesag(Message$)
FOR I%L=0 TO 7
Msg%L(I%L)= CVI( MID$(Message$,I%L*2+1))
NEXT I%L
IF Msg%L(0)=Wm_Arrowed%L THEN ' Pfeil wurde angeklickt
IF Msg%L(4)=Wa_Upline%L THEN 'Pfeil nach oben
Scroll_Up(Topline%L)' 1 Zeile nach oben
ENDIF
IF Msg%L(4)=Wa_Dnline%L THEN 'Pfeil nach unten
Scroll_Dn(Topline%L) ' 1 Zeile nach unten
ENDIF
ENDIF
UNTIL Msg%L(0)=Wm_Closed%L'...bis Closer angeklickt wird
Wind_Close(W_Handle%L)
RETURN
'--------------------------------------------------------
DEF PROC Show_Text(R Topline%L) ' Text-Anfang ausgeben
LOCAL I%L
TEXT HEIGHT =13' normale Textgröße
TEXT STYLE =0' sonst wird die Textausgabe zu langsam
FOR I%L=0 TO 9
TEXT 16,((13-I%L)*16+13),Zl$(10-I%L)
TEXT 16,((14+I%L)*16+13),Zl$(11+I%L)
NEXT I%L
Topline%L=l' oberste Zeile ist Zeile 1
RETURN
'--------------------------------------------------------
DEF PROC Scroll_Up(R Topline%L)'
LOCAL Adr%L,Scr_Dn%L
MOUSEOFF ' sonst gibt's viele Mauspfeile
REPEAT ' solange scrollen, ...
IF Topline%L=1 THEN EXIT ' Text-Anfang erreicht
'mit Basic-Befehlen:
'FOR Adr=29442 TO 5122 STEP -80'nach unten schieben
'MEMORY_MOVE Scr+Adr,74 TO Scr+Adr+1280
'NEXT Adr
'oder Assembler:
INLINE "22790000044E43E9730245E90500303C012F4CD9013E48D2013E4CD9013E4 8EA013E00184CD9013E48EA013E00303559004843E9FF6645EAFFB051C8FFD6"
Topline%L=Topline%L-1' oberste Zeile 1 Zeile höher
TEXT 16,77,Zl$(Topline%L)' neue oberste Zeile
TEXT 64,57, STR$ (Topline%L)+" " ' Zeile anzeigen
UNTIL MOUSEBUT =0' ...bis Mausknopf losgelassen wird
MOUSEON
RETURN
'--------------------------------------------------------
DEF PROC Scroll_Dn(R Topline%L)
LOCAL Adr%L
MOUSEOFF
REPEAT ' solange scrollen, ...
IF Topline%L=Lines%L-20 THEN EXIT ' Text-Ende erreicht
'mit Basic-Befehlen:
'FOR Adr=6402 TO 30722 STEP 80' nach oben schieben
'MEMORY_MOVE Scr+Adr,74 TO Scr+Adr-1280
'NEXT Adr
'oder Assembler
INLINE ”22790000044E43E9190245E9FB00303C012F4CD9013E48D2013E4CD9013E48EA013 E00184CD9013E48EA013E00303559004843E9000645EA005051C8FFD6"
Topline%L=Topline%L+1' oberste Zeile 1 Zeile tiefer
TEXT 16,381,Zl$(Topline%L+20)' neue unterste Zeile
TEXT 64,57,STR$(Topline%L)+" "' Zeile anzeigen
UNTIL MOUSEBUT =0' ...bis Mausknopf losgelassen wird
MOUSEON
RETURN
'--------------------------------------------------------
DEF PROC Show_Window(W_Handle%L,W%L,H%L,Name$, Info$)'
LOCAL X%L,Y%L,W%L,H%L
X%L=(640-W%L)/2 ' hor. zentrieren
Y%L=((400-H%L)/2)+9' ver. zentrieren, +9 wegen Menüzeile
Wind_Set(W_Handle%L,Wf_Name%L,Name$,Name_Mem%L)'Titelzeile
Wind_Set(W_Handle%L,Wf_Info%L,Info$,Info_Mem%L)'Infozeile
Wind_Open(W_Handle%L,X%L,Y%L,W%L,H%L)'Fenster öffnen
Wind_Get(W_Handle%L,Wf_Workxywh%L,Wx%L,Wy%L,Ww%L,Wh%L)'Gr. holen
MOUSEOFF
FILL STYLE =0,1' Füllmuster = weiße Fläche
PBOX Wx%L-1,Wy%L-1,Ww%L+2,Wh%L+2'Fensterinhalt löschen
MOUSEON
RETURN
'========================================================
' Hier müssen die Prozeduren der GEM-Library stehen !
'--------------------------------------------------------