/* Die folgenden drei Funktionen stammen aus /usr/X11/mit/lib/Xaw/Scrollbar.c Die letzte Funktion "Redisplay" ist die Expose-Methode des Athena Scrollbar Widgets. Sie benutzt "PaintThumb", welche ihrerseits "FillArea" benutzt. */ /* "FillArea" füllt im Scrollbarfenster w den Höhenbereich von top bis bottom, wobei die 'Höhe' oder 'Tiefe' bei liegendem Bar die Horizontale bezeichnet. "thumb" ist dabei ein Flag, ob der Bereich gesäubert werden soll (0) oder der Schieber gezeichnet werden soll (1) */ static FillArea(w, top, bottom, thumb) ScrollbarWidget w; Position top, bottom; int thumb; /* Flag clear/draw? */ { Dimension length = bottom-top; /* Länge des Rechtecks */ if (bottom < 0) return; /* Beim Expose soll ja nichts gelöscht werden, darum wird dort ein negativer "bottom" übergeben, der keine Operation verursachen soll. Hätten die auch sauberer schreiben können. */ switch(thumb) { /* Fallunterscheidung: zeichnen oder löschen? Ginge auch mit "if" */ case 1: if (w->scrollbar.orientation == XtorientHorizontal) XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc, top, 1, length, w->core.height-2); /* es wird an den Rändern je ein Pixel frei gelassen, damit nicht der Eindruck entsteht, der Schieber klebe am Rand, daher hier die 1 und die -2. Im gc ist ein Grafikkontext mit Stipple-Muster, damit der Schieber grau erscheint. */ else XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc, 1, top, w->core.width-2, length); /* war das Gleiche in grün, gedreht für vertikale Scrollbars */ break; case 0: /* ... und nochmal fürs Löschen: */ if (w->scrollbar.orientation == XtorientHorizontal) XClearArea(XtDisplay(w), XtWindow(w), top, 1, length, w->core.height-2, FALSE); else XClearArea(XtDisplay(w), XtWindow(w), 1, top, w->core.width-2, length, FALSE); /* Das FALSE sagt, daß das Clear kein Expose generieren soll. */ } } /* "PaintThumb" berechnet eine neue Scrollbarhöhe aus den gebrochenen Komponenten "top" (wieviel Anteil des Buffers ist über dem Fenster?) und "shown" (wieviel Anteil des Buffers ist das Angezeigte?), und zeichnet nur die Änderungen ein, d. h. dazugekommene oder zu löschende Bar-Bereiche. Dazu muß der aktuell angezeigte Bereich noch in den Komponenten topLoc und shownLength gespeichert sein. */ static void PaintThumb( w ) ScrollbarWidget w; { Position oldtop, oldbot, newtop, newbot; /* bisherige Position merken: */ oldtop = w->scrollbar.topLoc; oldbot = oldtop + w->scrollbar.shownLength; /* neue Position berechnen: */ newtop = w->scrollbar.length * w->scrollbar.top; /* Hier wurde implizit float nach int gecastet */ newbot = newtop + (int)(w->scrollbar.length * w->scrollbar.shown); /* und hier explizit. */ if (newbot < newtop + (int)w->scrollbar.min_thumb) newbot = newtop + w->scrollbar.min_thumb; /* Der Schieber darf nicht so klein geraten, daß man ihn nicht mehr sieht, oder ihn nicht mehr anfassen kann */ /* Nun kann man das Ergebnis eintragen in "topLoc" und "shownLength", um beim nächsten "PaintThumb" die aktuelle Position noch zu wissen */ w->scrollbar.topLoc = newtop; w->scrollbar.shownLength = newbot - newtop; if (XtIsRealized((Widget)w)) { /* Abfrage, weil man in unsichtbare Fenster besser nicht zeichnet, wobei mir unklar ist, warum sie diesen Event bekommen können. */ /* Reicht der Bar weiter nach oben? Dann ergänzen: */ if (newtop < oldtop) FillArea(w, newtop, MIN(newbot, oldtop), 1); /* Startet der Bar nun tiefer? Dann etwas abschneiden: */ if (newtop > oldtop) FillArea(w, oldtop, MIN(newtop, oldbot), 0); /* Reicht der Bar weniger tief? Dann etwas abschneiden: */ if (newbot < oldbot) FillArea(w, MAX(newbot, oldtop), oldbot, 0); /* Reicht der Bar unten weiter? Dann ergänzen: */ if (newbot > oldbot) FillArea(w, MAX(newtop, oldbot), newbot, 1); } } /* "Redisplay" ist im scrollbarClassRec als Exposemethode der Widgetklasse eingetragen, sie wird vom Toolkit aufgerufen, wenn der Scrollbar Exposure-Events erhalten hat, mit den Parametern Widget, Event-Information, und dem sichtbar gewordenen Bereich bzw. NULL, falls das ganze Widget erneuert werden soll. */ /* ARGSUSED */ static void Redisplay( gw, event, region ) Widget gw; /* Xt verlangt den allgemeinen Typ */ XEvent *event; /* wird hier ignoriert, deswegen ARGSUSED für lint */ Region region; /* habe ich gerade erläutert */ { /* reine Typenumwandlung, um auf die eigenen Komponenten zuzugreifen: */ ScrollbarWidget w = (ScrollbarWidget) gw; int x, y; unsigned int width, height; /* Position und Ausmaße des Bars wieder extrahieren: */ if (w->scrollbar.orientation == XtorientHorizontal) { x = w->scrollbar.topLoc; y = 1; width = w->scrollbar.shownLength; height = w->core.height - 2; } else { x = 1; y = w->scrollbar.topLoc; width = w->core.width - 2; height = w->scrollbar.shownLength; } /* Die Unterscheidung zwischen horizontal und vertikal und die Bedeutung der 1 und -2 wurde in der ersten Funktion schon erläutert */ if ( (region == NULL) || (XRectInRegion(region, x, y, width, height) != RectangleOut) ) { /* Also wenn das ganze Fenster oder ein Teil des Bars sichtbar wurde, dann wird gezeichnet: */ /* Als derzeitig angezeigter Bereich wird das Pseudointervall [ - (length + 1) ... -1] angegeben, damit "PaintThumb" den gesamten Bar neu zeichnet. "FillArea" ist schlau genug, da nicht im Negativen herumzuradieren. */ w->scrollbar.topLoc = -(w->scrollbar.length + 1); /* ... und ab die Post! */ PaintThumb( w ); } }