FORMA$FTXTTCPPBTCPHKCHRSr›DEinleitung›@ Jeder kennt die Windows mit den Proportionalgadgets im rechten und unteren Rand. Man sieht sie auf der Workbench, in Textverarbeitungs- programmen und -editoren oder in Requestern bei der Auswahl einer zu ladenden Datei (zB. bei Dpaint). Diese Windows oder Requester ermöglichen eine komfortable Benutzung, weil man sich mit Hilfe der Scrollgadgets jeden beliebigen Teil der Gesammtdaten anzeigen lassen kann, und man an den Proportionalgadgets gleichzeitig ablesen kann, wie groß der Ausschnitt ist, den man gerade sieht. Besonders nützlich ist dies, wenn die Windowgröße veränderlich ist, und bei kleinem Window nicht mehr alle Daten hineinpassen (siehe Workbench-Windows) oder, wie bei einem Textverarbeitungsprogramm, eine Textseite von vorneherein viel größer als eine Bildschirmseite ist. Wer selbst Programme schreibt, schreckt vielleicht vor der komplizierten Programmierung solcher Scrollwindows zurück und verwendet lieber eine einfacher zu programmierende Technik. Hierunter leidet die Benutzerfreundlichkeit des Programms, und das muß wirklich nicht sein, vor allem seit es "Superlists" gibt. Mit diesem Modul beschränkt sich der Aufwand auf die Initialisierung und Verwaltung der Daten. Wie diese angezeigt werden, wie gescrollt wird ist Sache von "SuperLists". ›DSo wird's gemacht›@ Bevor man eine SuperList erzeugt, sollte man sich über folgendes im Klaren sein: TCPHKCHRS:w* In welchem Window(-rastport) oder Requester(-rastport) soll die Liste angezeigt werden * Welche Hintergrundfarbe hat die Liste * Wie sollen die Proportionalgadgets für vertikales und horizontales Srolling aussehen * Wo und wie groß soll die Liste im Window (Requester) angezeigt werden * Welche Daten hat der zu dem verwendeten RastPort gehörige Zeichensatz (Achtung, es darf keine Proportionalschrift sein) * Welcher Ausschnitt soll voreinstellungsgemäß angezeigt werden Anmerkung: Die aktuelle Version von SuperLists ist auf Textdaten, die in vertikaler ›Dund›@ horizontaler Richtung gescrollt werden können, beschränkt. Falls jemand das Programm so verändern will, daß es auch Grafik (wie zB. die Icons in den Workbench-Windows) beherrscht, oder daß Listen, die nur in eine Richtung gescrollt werden, erzeugt werden können, bin ich gerne bereit, eventuelle Fragen zu beantworten. ›DErzeugen der Scrollgadgets›@ Jede SuperList benötigt zwei Proportionalgadgets, von denen das eine vertikal, das andere horizontal beweglich ist. Diese Gadgets müssen erzeugt werden, bevor die Liste initialisiert wird. Es wird hier auf eine Beschreibung, wie dies geschieht, verzichtet. Dies kann man zB. im "Intuition Reference Manual" nachlesen. ›DInitialisieren der Liste›@ Eine SuperList besteht aus einem Listenkopf (SuperList-RECORD) und null oder mehreren Einträgen (Entry-RECORDs). Der Listenkopf enthält Parameter, die das Aussehen der Liste beschreiben, und solche, die vom SuperList-Modul zur Verwaltung der Liste benötigt werden, sowie eine Exec.List zur Verwaltung der Einträge. Ein Eintrag enthält einen Exec.Node zur verknüpfung der Einträge untereinander und den eigentlichen Text (in Entry.node.name) sowie Parameter, die das Aussehen des Textes beschreiben (Farbe, DrawMode). Bevor die Liste das erste mal verwendet werden kann, müssen folgende Paramter in der SuperList-Struktur initialisiert werden: * rastPort - Zeiger auf den RastPort des Windows oder Requesters, in dem die Liste angezeigt werden soll. * backPen - Hintergrundfarbe der Liste * propX - Zeiger auf das Proportionalgadget für horizontales Scrolling * propY - Zeiger auf das Gadget für vertikales Scrolling * leftEdge - linker Rand der Listenanzeigefläche relativ zum linken Rand des Windows oder Requesters (Einheit: Pixel) * topEdge - oberer Rand relativ zum Rand des Windows/Requesters * width - Breite der Anzeigefläche (Einheit: Pixel), muß mindestens so groß wie FontXsize+1 sein (s. unten) * height - Höhe der Anzeigefläche, muß mindestens FontYsize sein * FontXsize - Breite eines Zeichens in Pixel (kein Proportional- schriftsatz!) * FontYsize - Höhe einer Textzeile in Pixel(muß nicht unbedingt mit der Höhe eines Zeichens übereinstimmen, wenn zB. zusätzlicher Zeilenabstand erwünscht ist) * FontBaseLine - besagt, auf welcher Höhe die Grundlinie der Zeichen innerhalb einer Zeile liegen soll, sollte ungefähr gleich dem baseLine-Paramter des Fonts gesetzt werden * dispRow - Nummer des ersten Eintrags (Zeile) des zuerst anzuzeigenden Ausschnitts (0 ist der erste Eintrag) * dispColumn - Nummer der ersten Spalte des Ausschnitts (0 zeigt ab der ersten Spalte) * Die Exec.List muß initialisiert werden. Dies geschieht am besten mit ExecSupport.NewList(ADR(SuperList.list); . ›DAlle anderen Felder sollten nie verändert werden›@, sie dienen der internen Verwaltung. Es ist aber vielleich manchmal interessant, sie zu lesen, deshalb hier ihre Bedeutungen: * rows - Anzahl der vorhandenen Einträge (Zeilen) * columns - Anzahl der Spalten = Länge des längsten Eintrags (in Zeichen, nicht in Pixel) * topEntry - Zeiger auf den Node des obersten angezeigten Eintrags * bottomEntry - Zeiger auf den untersten angezeigten Eintrag (Vorsicht: Falls keine Einträge vorhanden sind, müssen top- und bottomEntry nicht unbedingt NIL sein. Diese Zeiger sollten möglichst nicht für eigene Zwecke verwendet werden.) * effWidth - Breite der Anzeigefläche in Zeichen (nicht in Pixel) * effHeight - Höhe in Zeichen Nachdem alles ordnungsgemäß initialisiert ist, wird RethinkList( SuperListPtr, NewList) aufgerufen, wobei SuperListPtr ein Zeiger auf den SuperList-RECORD und NewList eine von SuperLists exportierte Konstante ist. Um den aktuellen Ausschnitt der Liste anzuzeigen, verwendet man RefreshList(SuperListPtr). Man kann die Liste jetzt mit den Prozeduren ScrollList, InsertEntry, RemoveEntry, GetEntry, SetProp, GetProp, ClickRow, MakeEntry, RedrawEntry, RethinkList und RefreshList bearbeiten. ›DRefreshList›@ Diese Prozedur wird benötigt, um die Paramter zur interenen Verwaltung im Listenkopf neu zu berechnen. Normalerweise werden diese beim Aufruf von Prozeduren wie zB. InsertEntry automatisch aktualisiert. Es ist jedoch manchmal nötig, einige Parameter "von Hand" zu ändern, wenn zB. der Benutzer die Windowgröße verändert hat, und sich damit auch die Anzeigefläche der Liste verändert. Um das SuperLists-Modul davon in Kenntnis zu setzen, daß Parameter verändert wurden, sodaß es seine internen Parameter neu berechnen kann, wird RethinkList verwendet. Der Parameter New (newList oder oldList) besagt, inwieweit die Listenparameter rekonstruiert werden müssen. RethinkList(...,oldList) wird verwendet, wenn die Veränderung nur die Anzeigefläche betrifft (zB. Höhe, Breite, Zeichensatz usw.). Die Ausführungszeit ist im Gegensatz zu RethinkList(...,newList) relativ kurz. Bei letzterem wird nämlich die ganze Liste der Einträge durchgegangen und Zeilen und Spalten werden neu gezählt. Dies ist notwendig, wenn die Einträge mit anderen Prozeduren als die von SuperLists bearbeitet wurden, sodaß sich die Texte der Einträge verändert haben, oder Eiträge entfernt oder hinzugefügt wurden. Ein weiterer Fall ist das Erzeugen einer neuen Liste (siehe oben) oder ein Spezialfall von RemoveEntry (siehe dort). Wegen der besonders bei sehr großen Listen langen Ausführungszeit sollte RethinkList(...,newList) möglichst vermieden werden. Um einen einzelnen Eintrag abzuändern, ist es besser, ihn mit RemoveEntry zu entfernen und nach der Änderung mit InsertEntry wieder einzufügen. Folgende Parameter dürfen verändert werden, ohne daß RethinkList benötigt wird: SuperList.backPen, SuperList.FontBaseLine, Entry.frontPen, Entry.back- Pen, Entry.drawMode, Entry.userFlags, Entry.userData . ›DAchtung›@: Wenn Parameter (außer den oben genannten) einer SuperList-Struktur "von Hand", also nicht über SuperLists-Prozeduren, verändert werden, so darf keine andere SuperLists-Prozedur aufgerufen werden, bevor nicht RethinkList ausgeführt wurde. Die Prozedur findet sonst ungültige Werte vor, was zu einer chaotischen Anzeige und schlimmstenfalls sogar zum Systemabsturz führen kann. Falls von mehreren Tasks aus auf eine Liste zugegriffen wird, muß das Programmstück von der Parameterveränderung bis RethinkList im Forbidden-Status ablaufen. RethinkList korrigiert soweit als möglich ungültige Werte. Wenn zB. der aktuelle Anzeigeauschnitt über das Ende der Liste hinaus positioniert wurde, so wird der Ausschnitt auf die tiefstmögliche Position gesetzt. Diese Gutmütigkeit hat aber ihre Grenzen. So sollte man zB. keine Zeiger der Exec.List "verbiegen" oder eine negative Anzeigenbreite einstellen, weil solche "Scherze" meist unvorhersehbare Folgen haben. ›DRefreshList›@ Wie schon der Name sagt, dient diese Prozedur dem Auffrischen, dh. dem Neu-Anzeigen, des sichtbaren Listenausschnitts. Dies wird in drei Fällen benötigt: * wenn die Liste zum ersten mal angezeigt werden soll, * wenn sich die Liste in einem SimpleRefresh-Window befindet und man eine Message vom Typ "refreshWindow" erhält oder * wenn sich die Größe der Anzeigefläche verändert hat. Es ist auch dann ein Auffrischen erforderlich, wenn die Fläche kleiner geworden ist. Im Fall 1 und 3 ist auch noch zusätzlich SetProp() erforderlich (siehe dort). ›DRedrawEntry›@ Zeigt einen Eintrag (Zeile) neu an, sofern sich dieser im gerade sichtbaren Ausschnitt befindet. Dies ist notwendig, wenn zB. der Text, die Farbe oder der DrawModus dieses Eintrags verändert wurde. Falls sich die Länge des Texts verändert hat muß außerdem RethinkList() aufgerufen werden (siehe oben). Es ist dabei möglich, vorher einen alternativen Zeichensatz (zB. kursiv) zu setzen (mit Graphics.Set- Font() ), um diesen Eintrag besonders hervorzuheben. Dieser muß (!) aber die gleiche Zeichengröße wie der vorher verwendete haben (FontXsize, FontYsize). ›DScrollList›@ wird verwendet, um die Liste Zeichenweise in eine beliebige Richtung zu scrollen. Dies wird besonders bei Texteditoren benötigt, oder wenn die beiden Proportionalscrollgadgets noch mit je zwei Boolgadgets kombiniert sind (siehe zB. die kleinen Pfeile in den Ecken der Workbench-Windows). Das Scrolling läuft relativ schnell (weil blitterunterstützt) ab, und es wird nur der beim Verschieben der BitPlane-Daten freigelegte Abschnitt neu gezeichnet. Es wird damit möglich, Texteditoren zu bauen, bei denen mann nicht ewig warten muß, wenn man sich mit dem Cursor über den Windowrand hinausbewegt hat (sowas soll's auch geben!). Es ist nicht möglich, über das Ende oder vor den Anfang der Liste zu scrollen. Dies wird automatisch erkannt und abgefangen. "Dir" gibt die Richtung an, mit der sich das imaginäre Fenster über die Liste bewegt. "Left" bedeutet also, die sichtbaren Daten werden eigentlich rechtsverschoben, links wird ein Zeichen mehr sichtbar, rechts verschwindet eins. ›DMakeEntry›@ Bevor irgendetwas angezeigt oder gescrollt werden kann, müssen erst einmal Einträge in der Liste vorhanden sein. Meistens werden diese dynamisch alloziert, was am besten mit der Prozedur MakeEntry geschieht. Es wird ein Zeiger auf einen auf Null endenden String, Vorder- und Hinergrundfarbe und der gewünschte DrawModus übergeben. MakeEntry alloziert dann Speicher für den Entry-RECORD und den Text (einschließlich abschließender Null), kopiert den Text in den neuen Speicher und initialisiert den Entry-RECORD. Dabei wird die Länge des Textes (ohne Null) im Feld node.pri abgelegt. Will man Einträge anders als mit MakeEntry erzeugen, so ist es wichtig, dies ebenfalls zu tun, da die Länge für die weitere Verwaltung der Liste nötig ist. Die Felder userFlags und userData werden nicht benutzt und können vom Programmierer für beliebige Zwecke verwendet werden. Ist nicht mehr genügend freier Speicher vorhanden, wird NIL zurückgegeben. ›DInsertEntry›@ wird verwendet, um an der Stelle (Zeile) "Row" den Eintrag "Entry" in die Liste einzufügen. der Parameter "hidden" besagt, ob der Einfügevorgang sichtbar (s. Konstante "showIt") oder unsichtbar (hideIt) erfolgen soll. Dies kann wünschenswert sein, wenn mehere Einträge auf einmal eingefügt werden sollen. Es wäre umständlich und würde eventuell zu viel Zeit benötigen, nach jeder eingefügten Zeile die Liste neu anzuzeigen. Stattdessen erfolgt das einfügen der Zeilen unsichtbar. Erst die letzte Zeile wird sichtbar eingefügt, womit die gesammte Veränderung sichtbar wird. Einfügen außerhalb des gerade sichtbaren Ausschnitts der Liste erfolgt natürlich immer unsichtbar, unabhängig vom Parameter "hidden". Wird bei "Row" ein negativer Wert oder ein Wert, der hinter dem Ende der Liste liegt, angegeben, dann wird am Ende der Liste angehängt. ›DRemoveEntry›@ Diese Prozedur entfernt einen Eintrag aus der Liste. "Row" bezeichnet die Nummer des zu ertfernenden Eintrags (Zeile). Der Parameter "hidden" wird wie bei InsertEntry gehandhabt. Mit dem Parameter "mustRethink" hat es folgendes auf sich: Wird der längste Eintrag aus der Liste entfernt, ändert sich die Gesammtbreite der Liste, und diese muß mit RethinkList neu bestimmt werden. Dies wird nicht automatisch durchgeführt, da es Fälle gibt, in denen das Aufrufende Programm besser weiß, wann dies nötig ist und wann nicht. Es ist zB. denkbar, daß bei einem Textverarbeitungs- programm alle Zeilen einer Seite sowieso die gleiche maximale Länge haben (DIN A4), und es deshalb Performance-Verschwendung wäre, jedesmal RethinkList durchzuführen. Wird also bei RemoveEntry festgestellt, daß der entfernte Eintrag der Längste war, dann wird "mustRethink" TRUE, ansonsten bleibt es unverändert. Das bedeutet zwar, daß dieses Flag vor dem (ersten von mehreren) Aufruf(en) gelöscht werden muß, ermöglicht aber eine Art Oder-Verknüpfung bei mehreren RemoveEntry-Aufrufen hintereinander. ›DGetEntry›@ ermöglicht es, die Adresse des Entrys mit der Numer "Row" zu suchen. Falls der Eintrag vorhanden ist, wird ein Zeiger auf ihn zurückgegeben, ansonsten ist das Ergebnis NIL. Es wird darauf hingewiesen, daß diese Suche unter Umständen lange dauern kann, falls der gesuchte Eintrag nicht im sichtbaren Ausschnitt liegt (dort wird zuerst gesucht). Besonders bei sehr großen Listen (deutlich mehr als 1000 Einträge) kann es länger dauern, weil dann die gesammte Liste durchgegangen wird. Dies gilt im übrigen auch für Insert- und RemoveEntry, die intern auch diese Prozedur verwenden. Ich glaube aber, daß dies kein wesentliche Nachteil ist, weil Superlists wohl meistens für weniger lange Listen verwendet werden wird. ›DClickRow›@ Mit dieser Prozedur kann festgestellt werden, welchen Eintrag der Benutzer gemeint hat, als er einen Punkt im Window der Liste angeklickt hat. Es werden die Koordinaten des Zeigers relativ zum Nullpunkt des RastPorts (des Windows oder Requesters) übergeben. Das Ergebnis ist dann die Nummer des Eintrags oder -1, falls sich der Mauszeiger außerhalb des Anzeigebereichs der Liste befand. ›DGetProp›@ Wenn festgestellt worden ist, daß der Benutzer eines der Proportionalgadgets bewegt hat, muß die Stellung der Gadgets ausgelesen und der gewünschte Ausschnitt der Liste dargestellt werden. GetProp erledigt ersteres, RefreshList letzteres. Die beiden Prozeduren wurden nicht zu einer zusammengefaßt, weil es möglich ist, daß der Programmierer zwischen beiden noch einiges ausführen möchte. Bei der Verwendung von Proportionalgadgets können nämlich Rundungsfehler auftreten, die sich darin äußern, daß beim Vor- oder Zurückblättern um eine Seite (Anklicken der Fläche neben dem Knob im Container) eine Zeile irrtümlicherweise übersprungen wird. So ist es möglich, vor RefreshList die Parameter des List-RECORDS entsprechend zu korrigieren (RethinkList nicht vergessen). Wird darauf verzichtet, ist RethinkList nicht notwendig, dh. RefreshList kann direkt nach GetProp aufgerufen werden. ›DSetProp›@ Hat sich die Größe der Liste oder des Windows verändert, oder wurde mit ScrollList gearbeitet, dann müssen Stellung (Pot) und Größe (Body) der (Auto-)Knobs der Proportionalgadgets aktualisiert werden. Dies kann mit der Prozedur SetProp geschehen, die die Pot- und Body-Variablen der Gadgets entsprechend dem Verhältnis der sichtbaren Daten zu den Gesammtdaten einstellt und danach Intuition.ModifyProp() aufruft. Sind weniger Einträge vorhanden, als das Window hoch ist bzw. alle Einträge kürzer sind als das Window breit ist, so werden die Body-Werte der zugehörigen Gadgets auf das Maximum gestellt, dh. der Knob füllt den ganzen Container.TCHFTCPHKCHRS°›DDokumentation zu "SuperLists" Version 1.0›@ Seite  Autor: Nicolas Benezan, Postwiesenstr.2, 7000 Stuttgart 60 Unterstützung von Text-Listen mit Scroll-Gadgets in Modula