#Titel Programmieren / Proportionalgadget mit BOOPSI #Logo gadget27:Pinsel/AG.Prog #Font Losse 16 #C31 Wie erzeugt man ein Proportionalgadget mit BOOPSI ? #Font topaz 8 #C21 BOOPSI steht für "Basic Object Oriented Programming System for Intuition". Mit Hilfe von BOOPSI lassen sich verhältnismäßig leicht Gadgets und Images erschaffen, die hier als Objekte bezeichnet werden. Im Gegensatz zu normalen Intuition Elementen verwalten sich BOOPSI Objekte weitgehend selbständig. Sie reagieren z.B. auf Benutzereingaben mit entsprechenden Änderungen ihrer grafischen Oberfläche, ohne daß der Programmierer sich um Details kümmern muß. Man kann verschiedene Objekte auch dazu veranlassen, selbständig mit- einander zu kommunizieren. Dem Programmierer wird also viel Arbeit abgenommen. Auf Einzelheiten des BOOPSI Konzepts soll hier nicht eingegangen werden. Hier soll nur die Erzeugung und Verwaltung eines "Scollers" erläutert werden, der verwendet wird, um den Inhalt eines Fensters horizontal oder vertikal zu verschieben. #Seitenende #C31 Erzeugung eines BOOPSI Objektes =============================== #C21 Alle Objekte werden mit der Intuitionfunktion NewObjectA() erzeugt: #C10 NewObjectA = -$27c (privclass,pubclassID,tagList) (a0-a2) #C21 NewObjectA() erwartet Angaben darüber, welche Art von Objekt erzeugt werden soll und wie es anfänglich aussehen soll. Da Proportionalgadgets in BOOPSI schon implementiert sind, setzen wir privclass auf NULL und übergeben als pubclassID einen Zeiger auf den String "propgclass". Das Aussehen wird durch eine taglist bestimmt, die aus Paaren von sogenannten Attributen und dazu- gehörigen Werten aufgebaut ist. Abgeschlossen wird die Liste mit TAG_END. Wir wollen einen vertikalen Scroller haben, der im rechten Fensterrand pla- ziert sein soll und unterhalb des oberen Fensterrandes beginnen soll. Wir wollen außerdem annehmen, daß mit dem Scroller ein Text gescrollt werden soll. Für die Festlegung der Startwerte brauchen wir Informationen über die Höhe der Titelleiste des Fensters und die Breite seines rechten Randes. Es ist deshalb zweckmäßig, das Fenster zuerst zu öffnen und dann erst NewObjectA() aufzurufen. Aus der Windowstruktur können wir nun die benötigten Werte auslesen. Da unser Scroller IN den rechten Fensterrand eingepaßt werden soll, muß dieser breit genug sein. Der Rand ist normalerweise nur 4 Pixel breit - zu schmal für einen Scroller. Eine nachträgliche Anpassung des Randes an die Breite des Scrollers nimmt Intuition NICHT vor. Deshalb fordern wir mit dem Flag WFLG_SIZEGADGET ein sizing gadget an, das bisher immer eine Breite von 18 Pixeln hatte. Verlassen kann man sich aber darauf nicht. Bei künftigen OS- Versionen könnte sich das ändern. Es gibt außerdem Programme , die die Standardwerte verändern. Deshalb ist es wichtig, die tatsächliche Breite aus der Windowstruktur zu ermitteln. Eine Tagliste könnte nun folgendermaßen aussehen: #C10 GA_Top, wd_BorderTop+1 GA_Width, wd_BorderRight-6 GA_RelRight, -(wd_BorderRight-3) GA_RelHeight, -(wd_BorderTop+Höhe sizing gadget+1) GA_RightBorder, TRUE GA_ID, 1 PGA_Freedom, FREEVERT PGA_NewLook, TRUE PGA_Borderless, TRUE PGA_Visible, Zahl der im Fenster sichtbaren Zeilen PGA_Total, Zahl der Textzeilen insgesamt PGA_Top, 0 ICA_Target, ICTARGET_IDCMP TAG_DONE #C21 Die feststehenden Werte für die links stehenden Attribute entnimmt man dem Manual (intuition/gadgetclass.h). Die rechts stehenden Werte sind teilweise zu berechnen, teilweise stehen sie fest. GA_ID kann man weglassen, wenn das Fenster nur das eine Gadget hat, ebenfalls PGA_Top. Dessen Wert beträgt zum Anfang 0 und kann später noch mit SetGadgetAttrsA() geändert werden. Den Wert für PGA_Visible erhält man, wenn man die innere Höhe des Fensters durch die Höhe des verwendeten Fonts dividiert. PGA_Freedom legt in diesem Fall fest, daß der sog. Container vertikal verschoben werden kann. PGA_NewLook sorgt in Verbindung mit PGA_Borderless für ein dreidimensionales Aussehen des Scrollers. Eine kleine Schwierigkeit stellt die Berechnung von GA_RelHeight dar, weil die Höhe des sizing gadgets nicht bekannt ist, wenn es nur in den rechten Fensterrand eingebunden ist. Man kann sich hier mit einem Wert von 10 behel- fen oder man geht die Gadgetliste durch und holt sich dort den Wert. Nach dem Aufruf von NewObjectA() erhält man einen Zeiger auf das neu erschaf- fene Objekt oder NULL, wenn irgendwas schiefgegangen ist. #Seitenende #C31 Einbinden des neuen Objektes in das Fenster =========================================== #C21 NewObjectA() erzeugt nur eine Struktur im Speicher. Das Gadget ist also noch nicht sichtbar. Dies erreichen wir mit AddGadget() bzw. AddGList() und RefreshGadgets() bzw. RefreshGList(). Danach können wir endlich das neue Gadget bewundern. Es reagiert schon auf Anklicken, der Container läßt sich verschieben (wenn PGA_Visible kleiner als PGA_Total), und es paßt sich ohne Zutun des Programms der Fenstergröße an. Das ist aber auch alles. Falls wir schon Text im Fenster ausgegeben haben, würde dieser sich noch nicht bewegen. Warum nicht? Weil das Bewegen des Textes Aufgabe des Programmes ist. Es muß vor allem wissen, was mit dem Gadget gerade angestellt wird und welche aktuelle Position der Container hat. #Seitenende #C31 Meldungen des Gadgets an das Programm ===================================== #C21 Um informiert zu werden, was mit dem Gadget gemacht wird, haben wir in unserer taglist das Attribut ICA_Target auf ICTARGET_IDCMP (-1) gesetzt. Das führt dazu, daß Intuition an den Window-Userport eine IDCMPUPDATE-Message sendet, wenn das Gadget betätigt wird. Eine weitere Vorraussetzung ist, daß das entsprechende IDCMP-Flag für das Fenster gesetzt ist. Wenn wir eine solche Message erhalten haben, gibt es drei verschiedene Möglichkeiten, den aktuellen Zustand des Gadgets in Erfahrung zu bringen. 1. Wir werten das IAddress-Feld der IntuiMessage aus. Dort befindet sich ein Zeiger auf eine taglist mit den Attributen des Gadgets. 2. Wir "mappen" das gewünschte Attribut auf ein spezielles Dummy-Attribut ISSPECIAL_CODE. Dann steht der Wert dieses Attributes (z.B. PGA_Top) im Code-Feld der IntuiMessage. Diese auf den ersten Blick elegante Lösung ist nach meinen Erfahrungn die schlechteste, weil man Bewe- gungen des Containers nur häppchenweise mitgeteilt bekommt. Das Programm hinkt bei schnellen Bewegungen des Containers hinterher. 3. Wir verwenden GetAttr(). Damit kann man den aktuellen Wert EINES bestimmten Attributes erfragen. Da Proportionalgadgets nur über PGA_Top, also die aktuelle Position des Containers, Auskunft geben, reicht das aus. Wir verwenden Möglichkeit 3 und rufen GetAttr() mit PGA_Top, einem Zeiger auf das Objekt und einem Zeiger auf eine Pufferbereich auf. In letzteren wird der aktuelle PGA_Top-Wert als Langwort geschrieben. Nun kommt die Hauptaufgabe des Programmes, nämlich zu reagieren und den Text entsprechend dem neuen Wert zu verschieben. Da UPDATE-Messages aber auch dann kommen, wenn das Gadget nur angeklickt, der Container also gar nicht verschoben wurde, muß man unbedingt mit dem alten Wert vergleichen. Bei Gleichheit gibt's nichts zu tun (außer auf die nächste Message zu warten). #Seitenende #C31 Meldungen vom Programm an das Gadget ==================================== #C21 Wenn im Programm vorgesehen ist, daß der Anwender den Text z.B. auch mit den Cursortasten scrollen kann oder wenn der Anwender die Fenstergröße verändert, muß das Gadget darüber informiert werden, damit es sich den geänderten Ver- hältnissen anpassen kann. Beim Scrollen des Textes mit den Tasten sollte sich z.B. PGA_Top ändern, damit der Container die aktuelle Position im Text richtig widergibt. Das Gadget (Objekt) merkt davon selbst nichts, weil es nicht betätigt wurde. Deshalb muß mittels SetGadgetAttrsA() der PGA_Top-Wert neu gesetzt werden. Falls die Fensterhöhe verändert wurde, ist PGA_Visible zu berechnen und ebenfalls mittel SetGadgetAttrsA() neu zu setzen. Beides kann mit einem Aufruf geschehen. Die Höhe des Gadgets paßt sich übrigens automatisch an. Darum brauchen wir uns zum Glück nicht zu kümmern. #Seitenende #C31 Abmelden des Gadgets/Objektes ============================= #C21 Wenn das Fenster geschlossen wird, muß das mit NewObjectA() erzeugte Objekt mit DisposeObject() wieder freigegeben werden. In den Programmbeispielen des Manuals wird diese Funktion aufgerufen, nachdem das Gadget mit Remove- GList() vom Fenster abgekoppelt wurde. Erst dann erfolgt der Aufruf von CloseWindow(). Es scheint aber keinen Nachteil zu haben, erst das Fenster zu schließen und dann DisposeObject() aufzurufen. Jedenfalls habe ich noch kein Abstürze erlebt, und der Speicher wird auch komplett freigegeben. #Seitenende #C31 Hinweis ======= #C21 Wir benutzten ein sizing gadget, um den rechten Fensterrand auf ausreichende Breite zu bringen. Was aber, wenn die Fenstergröße gar nicht verändert werden kann bzw. soll? Man könnte trotzdem ein sizing gadget verwenden, was aber irreführend wäre. Der User würde dann vergeblich daran herumziehen. Es gibt noch eine andere Lösung: Man bindet schon beim Öffnen des Fensters ein unsichtbares Gadget mit der gewünschten Breite und GACT_RIGHTBORDER und GFLG_HNONE|GFLG_RELRIGHT ein. Damit dieses Gadget nicht das später einzubindende Proportionalgadget über- deckt und damit inaktiviert, muß man es ganz niedrig machen (2 Pixel reichen, schließlich ist nur seine Breite für uns wichtig) und ober- bzw. unterhalb des Propgadgets plazieren. Alternativ kann man ihm auch dieselbe Position und Größe des (später einzu- bindenden) Propgadgets geben. Das Propgadget muß aber dann mit AddGadget() VOR dem unsichtbaren in der Gadgetliste eingereiht werden. Angenommen, das Fenster besitzt 3 Systemgadgets, so belegen diese die Positionen 0 bis 2 in der Liste. Für das Propgadget kann dann die Position 3 angegeben werden. #C31 Jürgen Klawitter