Index: src/Vlist.c
===================================================================
RCS file: /sources/gv/gv/src/Vlist.c,v
retrieving revision 1.7
diff -u -r1.7 Vlist.c
--- src/Vlist.c	8 Jun 2008 14:08:31 -0000	1.7
+++ src/Vlist.c	20 Nov 2008 17:13:19 -0000
@@ -4,6 +4,7 @@
 **
 ** Copyright (C) 1995, 1996, 1997 Johannes Plass
 ** Copyright (C) 2004 Jose E. Marchesi
+** modified 2008 by Bernhard R. Link
 ** 
 ** This program is free software; you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License as published by
@@ -34,6 +35,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <math.h>
 /*
 #define MESSAGES
 */
@@ -68,6 +70,8 @@
 
 #define offset(field) XtOffsetOf(VlistRec, field)
 static XtResource resources[] = { 
+   {XtNreportCallback, XtCReportCallback, XtRCallback, sizeof(XtPointer),
+	offset(vlist.report_callbacks), XtRCallback, (XtPointer) NULL },
    {XtNselectedShadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension),
 	offset(vlist.selected_shadow_width), XtRImmediate, (XtPointer) 1},
    {XtNmarkShadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension),
@@ -193,6 +197,7 @@
   if (vw->vlist.vlist) s = vw->vlist.vlist;
   vw->vlist.vlist = GV_XtNewString(s);
   c = vw->vlist.vlist;
+  vw->vlist.firstVisible = 0;
   vw->vlist.selected = -1;
   vw->vlist.highlighted = -1;
   vw->vlist.entries = (int)strlen(vw->vlist.vlist);
@@ -214,6 +219,17 @@
   values.graphics_exposures = False;
   vw->vlist.background_GC = XtGetGC((Widget)vw,(unsigned) GCForeground | GCGraphicsExposures,&values);
 
+  /* TODO: check if this works here in international mode, or if it has
+   * to be moved to Realize... */
+  if( vw->simple.international == True ) {
+    XFontSetExtents *ext = XExtentsOfFontSet(vw->label.fontset);
+    vw->vlist.yofs = (ext->max_ink_extent.y<0)?-ext->max_ink_extent.y:ext->max_ink_extent.y;
+    vw->vlist.ydelta = ext->max_ink_extent.height;
+  } else {
+    vw->vlist.yofs = vw->label.font->max_bounds.ascent;
+    vw->vlist.ydelta = vw->label.font->max_bounds.ascent + vw->label.font->max_bounds.descent;
+  }
+
   ENDMESSAGE(Initialize)
 }
 
@@ -318,16 +334,9 @@
   VlistWidget vw = (VlistWidget)w;
   char * s;
   int i;
-  int yofs, ydelta;
+  int yofs = vw->vlist.yofs, ydelta = vw->vlist.ydelta;
 
-  if( vw->simple.international == True ) {
-    XFontSetExtents *ext = XExtentsOfFontSet(vw->label.fontset);
-    yofs = (ext->max_ink_extent.y<0)?-ext->max_ink_extent.y:ext->max_ink_extent.y;
-    ydelta = ext->max_ink_extent.height;
-  } else {
-    yofs = vw->label.font->max_bounds.ascent;
-    ydelta = vw->label.font->max_bounds.ascent + vw->label.font->max_bounds.descent;
-  }
+  yofs -= ydelta * vw->vlist.firstVisible;
 
   BEGINMESSAGE1(PaintEntryString)
   s = vw->label.label;
@@ -378,6 +387,10 @@
     ENDMESSAGE(PaintMark)
     return(ret);
   }
+  if (entry < vw->vlist.firstVisible) {
+    ENDMESSAGE(PaintMark)
+    return 0;
+  }
   if (style<0) { /* highlighted */
     INFMESSAGE(highlighted entry)
     ulx = vw->vlist.hulx;
@@ -397,11 +410,10 @@
   }
   x = (Position) ulx;
   y = (Position) (((int) vw->label.label_y) +
-		  (entry*(vw->label.label_height))/vw->vlist.entries +
+		  ((entry - vw->vlist.firstVisible)*vw->vlist.ydelta) +
 		  uly);
   width = (Dimension)((int) vw->core.width - ulx + lrx);
-  height= (Dimension)(((int) vw->label.label_height)/vw->vlist.entries +
-		      - uly + lry + .5);
+  height= (Dimension)(vw->vlist.ydelta - uly + lry + .5);
   ss = XawSUNKEN;
   if (region == NULL || XRectInRegion(region,x,y,width,height) != RectangleOut) {
     if (erase) {
@@ -487,14 +499,17 @@
     char *vlist = vw->vlist.vlist;
     Boolean paint=False;
 
+    if (entry < vw->vlist.firstVisible)
+      return;
+
     if (vlist[entry] == '*') paint = True;
     if (paint || erase) {
       x = (Position) (VLIST_MARK_LEFT_INDENT);
       y = (Position) (((int) vw->label.label_y) +
 		      VLIST_MARK_VERTICAL_INDENT +
-		      (entry*(vw->label.label_height))/vw->vlist.entries);
+		      ((entry-vw->vlist.firstVisible)*vw->vlist.ydelta));
       width = (Dimension) (VLIST_MARK_WIDTH);
-      height= (Dimension)(((int) vw->label.label_height)/vw->vlist.entries + 0.5 - 2*VLIST_MARK_VERTICAL_INDENT);
+      height= (Dimension)(vw->vlist.ydelta + 0.5 - 2*VLIST_MARK_VERTICAL_INDENT);
       ss = XawSUNKEN;
       if (region == NULL || XRectInRegion(region,x,y,width,height) != RectangleOut) {
 	if (paint) {
@@ -530,7 +545,7 @@
   BEGINMESSAGE(PaintMarksOfEntries)
   PaintMarkOfEntry(w, region, vw->vlist.highlighted,-1, False);
   if (vw->vlist.allow_marks) {
-    int i=0;
+    int i= vw->vlist.firstVisible;
     while (i < vw->vlist.entries) {
       if (i != vw->vlist.highlighted)
 	PaintMarkMarkOfEntry(w, region, i, False);
@@ -553,14 +568,67 @@
 Region region;
 {
   Dimension width;
+  char *s, *o;
+  int i, y;
   VlistWidget vw = (VlistWidget)w;
+  XRectangle rectangle;
+
   BEGINMESSAGE(PaintVlistWidget)
+  /* better not allow the widget to grow that large, but that needs fixing
+   * of the Clip widget */
   shiftLabel(vw);
   PaintMarksOfEntries(w, event, region);
   width = vw->threeD.shadow_width;
+  o = vw->label.label;
+  s = vw->label.label;
+  i = vw->vlist.firstVisible;
+  if (s) while (i > 0 && (s = strchr(s,'\n'))) { s++; i--; }
+  /* This still fails when the list is too long and does not print anything.
+   * Though that is perhaps best fixed by making Clip to enforce a real window
+   * height...
+  vw->label.label = s;
   vw->threeD.shadow_width = 0;
   (*SuperClass->core_class.expose) (w, event, region);
-  vw->threeD.shadow_width =  width; 
+  vw->threeD.shadow_width =  width;
+  vw->label.label = o;
+  until Clip is extended, just manually draw each line:
+  */
+  if (region)
+    XClipBox(region, &rectangle);
+  else {
+    rectangle.x = 0;
+    rectangle.y = 0;
+    rectangle.width = vw->core.width;
+    rectangle.height = vw->core.height;
+    if( rectangle.height >= 0x4000 )
+	    rectangle.height = 0x3fff;
+  }
+  y = vw->label.label_y + vw->vlist.yofs;
+  i = 0;
+
+  while (s != NULL) {
+    char *nl = strchr(s, '\n');
+    int len;
+    if (nl)
+      len = nl - s;
+    else
+      len = strlen(s);
+    if (y - vw->vlist.yofs > rectangle.y + rectangle.height)
+      break;
+    if (y + (vw->vlist.ydelta - vw->vlist.yofs) >= rectangle.y) {
+      if( vw->simple.international == True )
+	XmbDrawString(XtDisplay(w), XtWindow(w), vw->label.fontset,
+		      vw->label.normal_GC, vw->label.label_x, y, s, len);
+      else
+	XDrawString(XtDisplay(w), XtWindow(w), vw->label.normal_GC,
+		    vw->label.label_x, y, s, len);
+    }
+    if (nl)
+      s = nl + 1;
+    else
+      s = NULL;
+    y += vw->vlist.ydelta;
+  }
   ENDMESSAGE(PaintVlistWidget)
 }
 
@@ -644,12 +712,14 @@
 
   BEGINMESSAGE(VlistEntryOfPosition)
   y = y - (int) vw->label.label_y;
-  if (vw->label.label_height > 0) {
+  if (vw->vlist.ydelta > 0) {
     if (y < 0) entry = -1;
-    else       entry = (vw->vlist.entries*y)/(int)vw->label.label_height;
+    else       entry = y/vw->vlist.ydelta;
   }
   if (entry >= vw->vlist.entries)
 	  entry = vw->vlist.entries-1;
+  if (entry >= 0)
+    entry += vw->vlist.firstVisible;
   IMESSAGE(entry)
   ENDMESSAGE(VlistEntryOfPosition)
   return(entry);
@@ -670,8 +740,9 @@
   float h;
   BEGINMESSAGE(VlistPositionOfEntry)
   *yuP = *ylP = (int) vw->label.label_y;
-  if (e>=0 && vw->vlist.entries > 0) {
-    h = (float)vw->label.label_height/(float)vw->vlist.entries;
+  if (e >= vw->vlist.firstVisible && vw->vlist.entries > 0) {
+    e -= vw->vlist.firstVisible;
+    h = vw->vlist.ydelta;
     *yuP += (int)((float)e*h);
     *ylP += (int)((float)(e+1)*h);
   }
@@ -828,3 +899,78 @@
   vlist_change_mark(w,entry,change,-1);
   ENDMESSAGE(VlistChangeHighlighted)
 }
+
+/*###################################################*/
+/* VlistGetFirstVisible */
+/*###################################################*/
+
+int VlistGetFirstVisible(Widget w)
+{
+  VlistWidget vw = (VlistWidget)w;
+
+  return vw->vlist.firstVisible;
+}
+
+/*###################################################*/
+/* VlistSetFirstVisible */
+/*###################################################*/
+
+void VlistSetFirstVisible(Widget w, int newf)
+{
+  VlistWidget vw = (VlistWidget)w;
+  unsigned int height;
+
+  BEGINMESSAGE(VlistSetFirstVisible)
+  if (newf < 0)
+    newf = 0;
+  else if (newf >= vw->vlist.entries)
+    newf = vw->vlist.entries - 1;
+  if (newf == -1)
+    return;
+  if (newf != vw->vlist.firstVisible) {
+    vw->vlist.firstVisible = newf;
+    /* better not allow the widget to grow that large, but that needs fixing
+     * of the Clip widget */
+    height = vw->core.height;
+    if( height >= 0x8000 )
+	    height = 0x3fff;
+    XFillRectangle(XtDisplayOfObject(w), XtWindowOfObject(w), vw->vlist.background_GC,
+		    vw->core.x, vw->core.y, vw->core.width, height);
+    Redisplay(w, NULL, NULL);
+    if (vw->vlist.report_callbacks)
+      XtCallCallbackList (w, vw->vlist.report_callbacks, (XtPointer)0);
+  }
+  ENDMESSAGE(VlistMoveFirstVisible)
+}
+/*###################################################*/
+/* VlistMoveFirstVisible */
+/*###################################################*/
+
+void VlistMoveFirstVisible(Widget w, int start, int ydiff)
+{
+  VlistWidget vw = (VlistWidget)w;
+  int ly;
+  int newf;
+
+  BEGINMESSAGE(VlistMoveFirstVisible)
+  ly = vw->vlist.ydelta;
+  fprintf(stderr, "move: start=%d ydiff=%d ly=%d\n", start, ydiff, ly);
+  if (ydiff >= 0)
+	  ydiff += ly/2;
+  else
+	  ydiff -= ly/2;
+  newf = start + ydiff/ly;
+  VlistSetFirstVisible(w, newf);
+  ENDMESSAGE(VlistMoveFirstVisible)
+}
+
+/*###################################################*/
+/* VlistScrollPosition */
+/*###################################################*/
+
+float VlistScrollPosition(Widget w)
+{
+  VlistWidget vw = (VlistWidget)w;
+
+  return vw->vlist.firstVisible/(float)vw->vlist.entries;
+}
Index: src/Vlist.h
===================================================================
RCS file: /sources/gv/gv/src/Vlist.h,v
retrieving revision 1.3
diff -u -r1.3 Vlist.h
--- src/Vlist.h	7 Jun 2008 18:39:19 -0000	1.3
+++ src/Vlist.h	20 Nov 2008 17:13:19 -0000
@@ -35,6 +35,7 @@
 
 #include "paths.h"
 #include INC_XAW(Label.h)
+#include INC_XAW(Reports.h)
 
 #define XawVlistAll     -13
 #define XawVlistEven    -12
@@ -131,5 +132,9 @@
     int
 #endif
 );
+extern int VlistGetFirstVisible(Widget);
+extern void VlistSetFirstVisible(Widget, int);
+extern void VlistMoveFirstVisible(Widget, int, int);
+float VlistScrollPosition(Widget);
 
 #endif /* _Vlist_h_ */
Index: src/VlistP.h
===================================================================
RCS file: /sources/gv/gv/src/VlistP.h,v
retrieving revision 1.3
diff -u -r1.3 VlistP.h
--- src/VlistP.h	7 Jun 2008 18:39:19 -0000	1.3
+++ src/VlistP.h	20 Nov 2008 17:13:19 -0000
@@ -69,6 +69,7 @@
   GC mark_background_GC;
   GC selected_background_GC;
   GC highlighted_background_GC;
+  int firstVisible;
   int selected;
   int highlighted;
   int hulx;
@@ -79,6 +80,9 @@
   int suly;
   int slrx;
   int slry;
+  int ydelta;
+  int yofs;
+  XtCallbackList report_callbacks;
 } VlistPart;
 
 typedef struct _VlistRec {
Index: src/actions.c
===================================================================
RCS file: /sources/gv/gv/src/actions.c,v
retrieving revision 1.17
diff -u -r1.17 actions.c
--- src/actions.c	2 Aug 2008 16:48:10 -0000	1.17
+++ src/actions.c	20 Nov 2008 17:13:19 -0000
@@ -379,6 +379,7 @@
 {
   static int xf,yf,xo,yo,xp,yp,dyo;
   static Boolean scroll_initialized=False;
+  static int startvisible;
   static Boolean scrolling=False;
   static int entryo = -1;
   Boolean toggle_mark=False;
@@ -397,8 +398,7 @@
     if (!app_res.reverse_scrolling) dy = -dy;
     m  = 1.3*((float)slider->core.height/(float)ph);
     if (m<=1) m = 1;
-    y = yp +(int)(m*dy);
-    if (y<dh) y=dh; if (y>0) y=0;
+    y = yp -(int)(m*dy);
     yo = (int) event->xbutton.y_root;
 
     ph = (int)panner->core.width; if (ph<1) ph = 1;
@@ -411,9 +411,12 @@
     if (x<dh) x=dh; if (x>0) x=0;
     xo = (int) event->xbutton.x_root;
 
-    if (x!=xp || y!=yp) {
-      ClipWidgetSetCoordinates(panner,x,y);
+    if (x!=xp) {
+      ClipWidgetSetCoordinates(panner,x,0);
       xp = x;
+    }
+    if (y!=yp) {
+      VlistMoveFirstVisible(newtoc, startvisible, y);
       yp = y;
     }
     if (abs(xf-xo) > 6 || abs(yf-yo) > 6) scrolling = True;
@@ -422,7 +425,8 @@
     scroll_initialized = True;
     scrolling = False;
     xp = (int) slider->core.x; xf = xo = (int) event->xbutton.x_root;
-    yp = (int) slider->core.y; yf = yo = (int) event->xbutton.y_root;
+    yp = 0; yf = yo = (int) event->xbutton.y_root;
+    startvisible = VlistGetFirstVisible(newtoc);
   }
   else if (!strcmp(params[0],"scrolloff")) {
     scroll_initialized = False;
Index: src/callbacks.c
===================================================================
RCS file: /sources/gv/gv/src/callbacks.c,v
retrieving revision 1.21
diff -u -r1.21 callbacks.c
--- src/callbacks.c	2 Sep 2008 11:42:43 -0000	1.21
+++ src/callbacks.c	20 Nov 2008 17:13:19 -0000
@@ -156,32 +156,36 @@
   Widget w;
   XtPointer client_data, call_data;
 {
-  int x,y;
-
   BEGINMESSAGE(cb_newtocScrollbar)
-  x = (int) newtocControl->core.x;
   if (((int)(intptr_t)client_data)==1) {
     int dy = (int)(intptr_t)call_data;
-    y = (int) newtocControl->core.y - dy;
+    VlistMoveFirstVisible(newtoc, VlistGetFirstVisible(newtoc), dy);
   } else {
     float *percent = (float *) call_data;
-    y = (int)(-*percent * newtocControl->core.height);
+    VlistSetFirstVisible(newtoc, (int)(VlistEntries(newtoc)**percent));
   }
-  ClipWidgetSetCoordinates(newtocClip, x, y);
   ENDMESSAGE(cb_newtocScrollbar)
 }
 
 /*##################################################################*/
-/* cb_newtocClipAdjust */
+/* cb_newtocVisibleAdjust */
 /*##################################################################*/
 
-void cb_newtocClipAdjust(w, client_data, call_data)
+void cb_newtocVisibleAdjust(w, client_data, call_data)
   Widget w;
   XtPointer client_data, call_data;
 {
+  int entries;
+
   BEGINMESSAGE(cb_newtocClipAdjust)
-  XawScrollbarSetThumb(newtocScroll,
-		       -(float)newtocControl->core.y/(float)newtocControl->core.height,
+
+  entries = VlistEntries(newtoc);
+
+  if (entries <= 0)
+    XawScrollbarSetThumb(newtocScroll, 0.0, 1.0);
+  else
+    XawScrollbarSetThumb(newtocScroll,
+			 VlistScrollPosition(newtoc),
 		       (float)newtocClip->core.height/(float)newtocControl->core.height);
   ENDMESSAGE(cb_newtocClipAdjust)
 }
Index: src/callbacks.h
===================================================================
RCS file: /sources/gv/gv/src/callbacks.h,v
retrieving revision 1.5
diff -u -r1.5 callbacks.h
--- src/callbacks.h	23 Aug 2008 18:18:55 -0000	1.5
+++ src/callbacks.h	20 Nov 2008 17:13:19 -0000
@@ -49,7 +49,7 @@
 #endif
 );
 
-extern void			cb_newtocClipAdjust (
+extern void			cb_newtocVisibleAdjust (
 #if NeedFunctionPrototypes
    Widget,
    XtPointer,
Index: src/main.c
===================================================================
RCS file: /sources/gv/gv/src/main.c,v
retrieving revision 1.44
diff -u -r1.44 main.c
--- src/main.c	24 Aug 2008 10:33:48 -0000	1.44
+++ src/main.c	20 Nov 2008 17:13:19 -0000
@@ -1128,11 +1128,11 @@
           cont_child[cont_child_num] = newtocFrame; cont_child_num++;
   									n=0;
           newtocClip = XtCreateManagedWidget("newtocClip", clipWidgetClass,newtocFrame, args, n);
-	     XtAddCallback(newtocClip, XtNreportCallback,cb_newtocClipAdjust, (XtPointer)NULL);
   									n=0;
           newtocControl = XtCreateManagedWidget("newtocControl", aaaWidgetClass,newtocClip, args, n);
          	      							n=0;
           newtoc = XtCreateManagedWidget("newtoc", vlistWidgetClass,newtocControl, args, n);
+	  XtAddCallback(newtoc, XtNreportCallback,cb_newtocVisibleAdjust, (XtPointer)NULL);
 									n=0;
           newtocScroll = XtCreateWidget("newtocScroll", scrollbarWidgetClass,control, args, n);
 	     XtAddCallback(newtocScroll, XtNscrollProc,cb_newtocScrollbar, (XtPointer)1);
Index: src/misc.c
===================================================================
RCS file: /sources/gv/gv/src/misc.c,v
retrieving revision 1.26
diff -u -r1.26 misc.c
--- src/misc.c	9 Sep 2008 17:11:04 -0000	1.26
+++ src/misc.c	20 Nov 2008 17:13:19 -0000
@@ -303,41 +303,43 @@
   XEvent *event;
   Boolean check_toc;
 {
-  int x,y,yl,yu,ny=99999;
+  int firstvisible, lastvisible;
   Boolean b = False;
   INFMESSAGE(misc_setPageMarker)
   if (toc_text && (entry >= 0)) {
-    if (kind == 0 && VlistSelected(newtoc) != entry)  
+    if (kind == 0 && VlistSelected(newtoc) != entry)
       VlistChangeSelected(newtoc,entry,XawVlistSet);
-    else if (kind == 1 && VlistHighlighted(newtoc) != entry)  
+    else if (kind == 1 && VlistHighlighted(newtoc) != entry)
       VlistChangeHighlighted(newtoc,entry,XawVlistSet);
     else if (kind == 2) {
       entry=VlistSelected(newtoc);
       if (entry<0) return;
     }
-    VlistPositionOfEntry(newtoc,entry,&yu,&yl);
-    x = newtocControl->core.x;
-    y = newtocControl->core.y;
-    IIMESSAGE(x,y)
-    IIMESSAGE(yu,yl)
-    IIMESSAGE(y,newtoc->core.y)
-    if (yu != yl) {
-      ny = -((int)newtoc->core.y + yu) + 14;
-      if (y<0 && y < ny) b = True;
-      if (!b) {
-	ny = (int)newtocClip->core.height - ((int)newtoc->core.y + yl + 14);
-        if (y>ny) b = True;
+    firstvisible = VlistGetFirstVisible(newtoc);
+    if (firstvisible > entry || (entry > 0 && firstvisible >= entry)) {
+      if (entry > 0)
+	VlistSetFirstVisible(newtoc, entry - 1);
+      else
+	VlistSetFirstVisible(newtoc, entry);
+      b = True;
+    } else {
+      /* sadly newtoc does not know it's height, so it cannot be told
+       * to made an item visible and we need to trick: */
+      lastvisible = VlistEntryOfPosition(newtoc, newtocClip->core.height);
+      if (entry > firstvisible && entry >= lastvisible) {
+	VlistSetFirstVisible(newtoc, entry - (lastvisible - firstvisible - 1));
+	b = True;
       }
-      if (b) {
-	INFIMESSAGE(jumping to,ny)
-	ClipWidgetSetCoordinates(newtocClip,x,ny);
-	if (event)
+    }
+    /* if this happened by mouse, we have the information to get the
+     * highlight up to date again: */
+    if (b) {
+      if (event)
 	{
-	   entry = VlistEntryOfPosition(newtoc,(int)event->xbutton.y+(y-ny));
-	   if (entry != VlistHighlighted(newtoc) && check_toc)
-		VlistChangeHighlighted(newtoc,entry,XawVlistSet);
-        }
-      }
+	  entry = VlistEntryOfPosition(newtoc, (int)event->xbutton.y);
+	  if (entry != VlistHighlighted(newtoc) && check_toc)
+	    VlistChangeHighlighted(newtoc,entry,XawVlistSet);
+	}
     }
   }
 }
