After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 1165 - Handle late setting of adjustments in scrollable widgets
Handle late setting of adjustments in scrollable widgets
Status: RESOLVED WONTFIX
Product: gtk+
Classification: Platform
Component: .General
1.2.x
Other other
: Normal normal
: Medium fix
Assigned To: gtk-bugs
gtk-bugs
Depends on:
Blocks:
 
 
Reported: 1999-04-28 23:50 UTC by Ewen McNeill
Modified: 2015-06-23 11:30 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Ewen McNeill 2001-01-27 19:46:20 UTC
Package: gtk+
Version: 1.2.1

Bug description:

When using a scrolled window widget (with a widget added with a viewport), 
that has been realised, if new adjustments are set (with set_hadjustment() 
and set_vadjustment()), then two display problems occur:

1.  The X server is not informed of the new position of view on the 
    inserted widget, and thus sends exposure events for the wrong
    part of the inserted widget.

2.  The scroll bars are not redrawn correctly until they have been
    unrealised and realised again.

The first problem is resolved as soon as the scroll bars are used;
things suddenly jump into the correct position.  As far as I can tell
this is because only the callback from the scroll bars being adjusted
calls gdk_window_move() (which in turn informs the X server with
XWindowMove()).  gdk_window_move() is not called by either
set_hadjustment() or set_vadjustment() (this is probably the best fix).

As a work around the scroll bars can be nudged (add one to the
adjustment current position, then take it off again); this is my current
work around and gives the correct display eventually (although some
unwanted redrawing is done -- this is noticeable in my instance because
the widget drawing code involves a lot of calculations and hence takes a
lot of time).

The second problem is resolved when, eg, the window is resized.  As a
work around I've been turning the scroll bars off and then on again
(setting policy = NEVER, then setting policy = AUTOMATIC).

Unfortunately both the nudging and the scroll bar unrealising/realising
need to be done with the widget in the viewport realised which results
in extra redrawing.  (The nudging, etc, does not take effect if the
inserted widget is not realised.)


Operating environment:

Debian slink, with manually installed Glib 1.2.1, GTK+ 1.2.1, GTK-- 1.0.

Linux 2.2.1 kernel
Xfree86 X server (3.3.2.3a) (SVGA, for Matrox Millennium (I) card)

Using GTK-- C++ wrapper for GTK+, from C++ code, compiled with EGCS 1.1.1.

The problem appears in both 1.2.1 (which I've done most of my testing
on), and as far as I can tell in a very brief test in 1.2.2.  The
1.2.1->1.2.2 patch doesn't indicate any changes in this area.

The GTK-- wrappers are simply calling the GTK+ functions directly (with
little other handling going on), and this and reading the GTK+ code are
the reason for thinking it is GTK+ not taking the correct steps.


Reproducing the bug:

Unfortunately I found these problems (and hacked around them) in the
middle of a large chunk of C++ code which I can't make available.  

I also haven't yet had the time to write up a small sample program that
exhibits the problem.

As far as I can tell the problem can be repeated by:

1. Creating a scrolled window, with a suitable widget inside it that
   reports on the expose events it is getting (I'm using a drawing
   area), and set the scroll bar policies to AUTOMATIC.
2. Realise everything.
3. Giving the scrolled window new adjustments with a different range
   (ie lower and upper) and a different value.  (In my instance I'm
   "zooming" so the range is normally 2-3 times as big, and the
   position is scaled appropriately.  But the problem seems to occur 
   irrespective of whether the adjustment range gets bigger or smaller.)

This should repeat the wrong-expose-events problem.  The scroll bar
issue also seems to occur in the same manner, but is most obvious when
attempts at scrolling around result in scrollbars that will not "scroll"
even though based on the size of the view and the size of the widget
inside it, it should scroll.  Resizing the window should "fix" this
problem.

(I realise it's a real pain not having example code that produces the
problem, and if you want to throw it back at me and say "provide an
example" then I'll do that.  It just might take a while.  Same with
patches.)

Ewen




------- Additional Comments From otaylor@redhat.com 1999-05-05 12:06:32 ----

Subject: GTK+: Scrolled window: set new adjustments doesn't inform X server
From: Owen Taylor <otaylor@redhat.com>
To: 1165@bugs.gnome.org
Cc: Ewen McNeill <ewen@naos.co.nz>
Message-Id: <ybek8uncxh3.fsf@fresnel.labs.redhat.com>
Date: 05 May 1999 12:06:32 -0400


An example program would in fact be most helpful here.
(Also, it would be preferred if it were straight 
GTK+ as opposed to GTK--)

Thanks,
                                        Owen




------- Additional Comments From ewen@naos.co.nz 1999-05-09 23:22:55 ----

Subject: Re: GTK+: Scrolled window: set new adjustments doesn't inform X server 
From: Ewen McNeill <ewen@naos.co.nz>
To: 1165@bugs.gnome.org
Message-Id: <199905100322.PAA07468@pagoda.wgtn.naos.co.nz>
Date: Mon, 10 May 1999 15:22:55 +1200

[This is a duplicate copy of a message that was bounced by the GTK
 bug tracking service:

Hi. This is the qmail-send program at tirania.nuclecu.unam.mx.
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.

<1165@bugs.gnome.org>:
/ Unknown ticket service address owen taylor <otaylor@bugs.gnome.org
 Recognised addresses are:
/     General:       Read # in Subject:        Ticket # is NNNN:
 ......

Owen: I've BCC'd you on this message, in order to avoid confusing the 
bug tracking service with your address, in case that was what had it
stumped.
]

In message <ybek8uncxh3.fsf@fresnel.labs.redhat.com>, Owen Taylor writes:
>An example program would in fact be most helpful here.
>(Also, it would be preferred if it were straight GTK+ as opposed to GTK--)

One example, in GTK+ 1.2.1.  The code is really hacked together but it
does show the problems that I reported in bug 1165, with one slight
variation: I've found it seems to be sufficient to hide/show the
_drawing_area_ (ie, contained widget) in order to get the scroll bars 
drawn correctly again (previously I reported it was necessary to turn 
the scroll bars off/on in the scrolled window).

The example code is loosely based on the scrolledwin demo example with
GTK+ 1.2.1, but has had bits hacked into it.  It should compile with the
standard sort of Makefile for those examples, viz:

-=- cut here -=-
CC = gcc

scrolledwin: scrolledwin.c 
	$(CC) `gtk-config --cflags` -g scrolledwin.c -o scrolledwin `gtk-config
 --libs`

clean: 
	rm -f *.o scrolledwin
-=- cut here -=-

When compiled and run, the program displays a scrolled window containing
a drawing area, and a small control bar at the bottom.  The drawing area
simply draws the position of the window in every so often across the
exposed area (makes it easy to tell what is being drawn).

The control bar contains:
1.  An "adjust" button, used to set the scrolled window scroll bar
    adjustments to random values (all less than 32767; an X limitation
    which I haven't seen documented in the GTK docs)

2.  Two checkboxes which can turn on and off the hacks I've been using:
    "hide/show" -- will hide the drawing area and show it again in order
                   to force a repaint (I don't think this should be
		   necessary when the scrolled window works properly)
    "nudge"     -- nudges the scroll bar after setting adjustments
                   (done by setting the value to one more than prev
		   value), which forces flush of position update through
		   to the X server.

To see the problems I reported:
1.  Compile and run the program.
    Click on adjust without selecting any checkboxes.
    Try the scroll bars.  Note how they don't scroll any more!

2.  Toggle the "hide/show" checkbox.
    Click on adjust.
    Note that the drawn area doesn't correspond with the area that
    the scroll bars have been adjusted to. 
    Click on one of the scroll bars.  Note how it suddenly springs
    into the correct position.  (For maximum effect click on the 
    move-by-1 arrow at the end of the scroll bar; a big jump for
    moving by 1 :-) )

3.  Toggle the "nudge" checkbox.
    Click on "adjust".
    Notice how it is now going to the correct place and redisplaying.

Situation 3 is how I believe it should behave without needing the
hide/show hack or the nudge scrollbar hack.  The very act of setting the
adjustments should flush a new position thorugh to the X server.
Although doing this when they have to be set independently is possibly
unfortunate (if the widget is visible it could be double redrawn), but
it should be done at least once without any hacks required in the
application.

If you've got any questions, etc, please feel free to get in touch.
Note that the code is a hack to demo the problem, and definitely leaks
memory, and has very poor style, because it was hacked together in a
hurry.  But these shouldn't affect the thing being demonstrated.

Source code follows.

Tested with Debian Slink (2.1), with Linux 2.2.1 kernel, with Glib 1.2.1 and
GTK+ 1.2.1.  C compiler is 

ewen@pagoda:/src/local/niwa/libs/gtk-bugs $ gcc -v
Reading specs from /usr/lib/gcc-lib/i486-linux/2.7.2.3/specs
gcc version 2.7.2.3

Bugs also noted with GTK-- (C++ wrapper for GTK+) using GTK-- 1.0, Glib
1.2.1 and GTK+ 1.2.1, and with a very brief test of the program using
GTK-- appeared to be there with Glib 1.2.2 and GTK+ 1.2.2  (The patch to 
GTK+ 1.2.2 didn't seem to affect files in the relevant areas.)

Ewen

-=- cut here -=-
/* Demonstration of bug in GTK+ 1.2.1 with scrolling windows, and 
 * changing adjustments:  (Reported as GTK bug 1165)
 * 1.  The X server is not informed of the new position of view on the 
 *     inserted widget, and thus sends exposure events for the wrong
 *     part of the inserted widget until the scroll bars are adjusted.
 *
 * 2.  The scroll bars are not redrawn correctly until they have been
 *     unrealised and realised again (or at least the drawing area
 *     hidden and shown).
 *
 * The "nudge scrollbar" toggle can be used to turn on and off the
 * workaround for the first problem.
 *
 * The "show/hide" toggle can be used to turn on and off the workaround
 * for the second problem.
 *
 * Hacked together by Ewen McNeill <ewen@naos.co.nz>, 1999/5/10,
 * based on: scrolledwin example.
 * 
 * NOTE: Quite a few of the bits in this code are just hacked together, with
 * just enough to show what goes wrong, and it'll, eg, leak memory.
 */
/* example-start scrolledwin scrolledwin.c */

#include <gtk/gtk.h>
#include <stdlib.h>

static int show_expose = 2;      /* 0 = don't; 1 = after config; 2 = always */

static GtkWidget *workaround_1_checkbox;   /* Workaround one: hide/show */
static int workaround_1 = 0;
static GtkWidget *workaround_2_checkbox;   /* Workaround two: nudge bar */
static int workaround_2 = 0;

static GtkWidget *scrolled_window;    /* Scrolled window area */
static GtkWidget *drawing_area;       /* Drawing area */

void destroy(GtkWidget *widget, gpointer data)
{
    gtk_main_quit();
}

/* Set new adjustments for the window.  We malloc the adjustments in
 * order to ensure they won't get overwritten, but leak them afterwards
 * for simplicity of this demo hack.  (This shouldn't affect the bug.)
 * 
 * The adjustments are set to random values in order to ensure they
 * change each time around.
 */
void new_adjustments(GtkWidget *widget, gpointer data)
{
  int mx = (int)(rand()*1.0*32767.0/(RAND_MAX*1.0));        /* 0..32767 */
  int my = (int)(rand()*1.0*32767.0/(RAND_MAX*1.0));        /* 0..32767 */
  int px = (int)((rand()*1.0)*(mx*1.0)/(RAND_MAX*1.0));     /* 0..mx */
  int py = (int)((rand()*1.0)*(my*1.0)/(RAND_MAX*1.0));     /* 0..my */

  GtkAdjustment *x_adjust = 
     (GtkAdjustment *)gtk_adjustment_new(px, 0, mx, 1, 10, 300);
  GtkAdjustment *y_adjust = 
     (GtkAdjustment *)gtk_adjustment_new(py, 0, my, 1, 10, 300);

  GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(scrolled_window);
  GtkDrawingArea    *da = GTK_DRAWING_AREA(drawing_area);

  printf("Adjusting X to %d/%d\n", px, mx);
  printf("Adjusting Y to %d/%d\n", py, my);
  gtk_drawing_area_size(da, mx, my); 

  gtk_scrolled_window_set_hadjustment(sw, x_adjust);
  gtk_scrolled_window_set_vadjustment(sw, y_adjust);

  /* Force a redraw (hide and show the drawing area) */
  if (workaround_1)
  {
    printf("Hiding and showing drawing area\n");
    gtk_widget_hide(drawing_area);
    gtk_widget_show(drawing_area);
  }

  if (workaround_2)
  {
    printf("Nudging scroll bar\n");
    gtk_adjustment_set_value(x_adjust, px+1);
  }
}

static gint
configure_event (GtkWidget *widget, GdkEventConfigure *event)
{
  printf("Configuring for %dx%d\n", widget->allocation.width,
                                    widget->allocation.height);
  if (! show_expose)
    show_expose = 1;
}

static gint
expose_event (GtkWidget *widget, GdkEventExpose *event)
{
  char posstr[128];
  int x, y;
  int minx, miny;
  int maxx, maxy;
  GdkFont *fixed_font;
  fixed_font = gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*");

  if (show_expose)
    printf("Expose event for %d,%d, of size %dx%d\n",
                    event->area.x, event->area.y,
                    event->area.width, event->area.height);

  if (show_expose < 2)
    show_expose = 0;

  /* Put some positioning kind of text into the window to show where 
   * we are in the window
   */
  minx = (event->area.x % 120 ? ((event->area.x/120)) * 120 : event->area.x);
  miny = (event->area.y % 50 ? ((event->area.y/50)) * 50 : event->area.y);
  maxx = event->area.x + event->area.width  + 120;
  maxy = event->area.y + event->area.height + 50;

  for (y = miny; y < maxy; y+=50)
  {
    for (x = minx; x < maxx; x+=120)
    {
      sprintf(posstr, "%d,%d", x, y);
      gdk_draw_text(widget->window, fixed_font, widget->style->black_gc,
	            x, y, posstr, strlen(posstr));
/*    printf("Putting string [%s] at %d,%d\n", posstr, x, y); */
    }
  }
}

void toggle_workaround_1(GtkWidget *widget, gpointer data)
{
  workaround_1 = !workaround_1;
  printf("Show/Hide is now %s\n", workaround_1 ? "on" : "off");
}

void toggle_workaround_2(GtkWidget *widget, gpointer data)
{
  workaround_2 = !workaround_2;
  printf("Nudge Scrollbar is now %s\n", workaround_2 ? "on" : "off");
}

int main (int argc, char *argv[])
{
    static GtkWidget *window;
    GtkWidget *table;
    GtkWidget *button;
    GtkWidget *hbox;
    char buffer[32];
    int i, j;
    
    gtk_init (&argc, &argv);

    /* Create the drawing area */
    drawing_area = gtk_drawing_area_new ();
    gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 600, 600);
    gtk_widget_show (drawing_area);

    /* Signals used to follow what is to be drawn */
    gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
			(GtkSignalFunc) expose_event, NULL);
    gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
			(GtkSignalFunc) configure_event, NULL);

    gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
			   | GDK_LEAVE_NOTIFY_MASK
			   | GDK_BUTTON_PRESS_MASK
			   | GDK_POINTER_MOTION_MASK
			   | GDK_POINTER_MOTION_HINT_MASK);
    
    /* Create a new dialog window for the scrolled window to be
     * packed into. A dialog is just like a normal window except it has a 
     * vbox and a horizontal separator packed into it. It's just a shortcut
     * for creating dialogs */
    window = gtk_dialog_new ();
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
			(GtkSignalFunc) destroy, NULL);
    gtk_window_set_title (GTK_WINDOW (window), "GtkScrolledWindow example");
    gtk_container_set_border_width (GTK_CONTAINER (window), 0);
    gtk_widget_set_usize(window, 300, 300);
    
    /* create a new scrolled window. */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    
    gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 10);
    
    /* the policy is one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS.
     * GTK_POLICY_AUTOMATIC will automatically decide whether you need
     * scrollbars, whereas GTK_POLICY_ALWAYS will always leave the scrollbars
     * there.  The first one is the horizontal scrollbar, the second, 
     * the vertical. */
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    /* The dialog window is created with a vbox packed into it. */	
							
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window, 
			TRUE, TRUE, 0);
    gtk_widget_show (scrolled_window);

    gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window
),
                                           drawing_area);
    gtk_widget_show (drawing_area);

    /* Add the control panel thingy to the bottom.
     * We have an "adjust" button to activate the change, and some 
     * toggle flags to turn on and off the hacks that work around
     * some of the problem.
     */
    button = gtk_button_new_with_label ("adjust");
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
		        (GtkSignalFunc) new_adjustments, NULL);
    
    hbox = gtk_hbox_new(FALSE, 0);
    gtk_widget_show(hbox);
    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);

    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), hbox, TRUE,
 TRUE, 0);

    workaround_1_checkbox = gtk_check_button_new_with_label("Hide/Show");
    workaround_2_checkbox = gtk_check_button_new_with_label("Nudge");
    workaround_1 = 0;
    workaround_2 = 0;
    gtk_box_pack_start(GTK_BOX(hbox), workaround_1_checkbox, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), workaround_2_checkbox, TRUE, TRUE, 0);
    gtk_signal_connect(GTK_OBJECT(workaround_1_checkbox), "toggled",
	               (GtkSignalFunc)toggle_workaround_1, NULL);
    gtk_signal_connect(GTK_OBJECT(workaround_2_checkbox), "toggled",
	               (GtkSignalFunc)toggle_workaround_2, NULL);

    gtk_widget_show (workaround_1_checkbox);
    gtk_widget_show (workaround_2_checkbox);
    gtk_widget_show (button);
    gtk_widget_show (window);
    
    gtk_main();
    
    return(0);
}
/* example-end */
-=- cut here -=-




------- Bug moved to this database by debbugs-export@bugzilla.gnome.org 2001-01-27 14:46 -------
This bug was previously known as bug 1165 at http://bugs.gnome.org/
http://bugs.gnome.org/show_bug.cgi?id=1165
Originally filed under the gtk+ product and general component.

The original reporter (ewen@naos.co.nz) of this bug does not have an account here.
Reassigning to the exporter, debbugs-export@bugzilla.gnome.org.
Reassigning to the default owner of the component, gtk-bugs@gtk.org.

Comment 1 Owen Taylor 2001-01-30 01:05:53 UTC
The problem here is, simply, put, gtk_viewport_set_[hv]adjustment()
doesn't work after the widget is set up. This will be rather tricky
to fix.
Comment 2 Owen Taylor 2001-02-22 22:06:09 UTC
I'm moving this from the 1.2.9 milestone to the 2.0 milestone,
because it is quite hard to fix, and it's not clear that the
fix would be compatible with what people expect from 1.2.8.

All scrolled widgets need to be checked for this, not
just GtkViewport.
Comment 3 Owen Taylor 2003-06-06 23:10:28 UTC
I've made a bunch of changes now that make it behave as I
expect, though I not quite as is expected here. The thing
to realize is that for a GtkViewport, the only free variable
that can be controlled by the application is the value -
the lower/upper/max/page_size/step_size are all under the
control of the widget and are in units of pixels.

Fri Jun  6 16:25:44 2003  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkviewport.c: Many fixes, along with extensive cleanups and
        refactoring of code to reduce duplication; fixes include:
 
        - gtk_viewport_realize(): Position the window correct from
adjustment
        values. (#110737, Michael Natterer)
 
        - Remove some division-by-zero checks in places where there is no
        longer division. (#110737)
 
        - gtk_viewport_class_init: Make the hadjustment/vadjustment
properties
        G_PARAM_CONSTRUCT, so that there will always be adjustments, even
        if gtk_viewport_new isn't used (#101135, Thomas Leonard).
 
        - Switch over to encapsulated lazy-creation for hadjustment/
        vadjustment; even with the CONSTRUCT property, we need this after
        destroy.
 
        - When updating the adjustment, immediate set their values to
        match the the current range of the viewport, and update the
        viewport position to match the value of the new adjustments.
        (Part of #1165)
Comment 4 Owen Taylor 2003-06-06 23:14:12 UTC
"make it behave" == "make GtkViewport behave" - other scrolled
widgets still need to be fixed.
Comment 5 Owen Taylor 2003-08-13 16:02:25 UTC
See 

http://mail.gnome.org/archives/gtk-devel-list/2003-June/msg00027.html

For discussion of the principles behind the changes.
Comment 6 Ewen McNeill 2004-10-09 01:36:14 UTC
I was the original bug reporter, back in 1999 (I've created a bugzilla account
just to be able to say this!). 

In the interveening _five_ years I've seen dozens of reassignments of this bug
to yet another later version of GTK+ (and/or Gnome).  I've long since ceased to
have anything to do with the project in which I encountered this bug (and that
project continues to work using the work around I came up with -- which is
described briefly above -- with some extra hacks to minimise the amount of
redrawing done).

Perhaps it would be more honest to mark this bug "won't fix" at this point.  

Or at least take me off the list of addresses notified of every deferrment of
this bug to a future release, as I really don't wish to receive a notification
of every single version that you have decided not to fix the bug in for yet more
years.

Ewen
Comment 7 Owen Taylor 2004-10-10 14:09:54 UTC
Sorry, there's no way to take you off the Cc: list since you are the original
reporter. Maybe you can come  up with a procmail rule or mail filter to discard
these messages. 

We've fixed this in *some* widgets now. It's still a legitimate TODO for
other widgets so WONTFIX'ing would be unfortunate.
Comment 8 Matthias Clasen 2004-11-30 16:35:35 UTC
Here is a list of scrollable widgets which still need to be fixed:

GtkTextView
GtkTreeView
GtkIconView
GtkLayout

less important, since deprecated, are

GtkCList
GtkText
Comment 9 Matthias Clasen 2004-11-30 16:37:45 UTC
Owens email linked above pretty clearly outlines what behaviour has to be
implemented.
Comment 10 Thomas D Ahle 2008-03-08 23:32:19 UTC
If this works, except for GtkCList and GtkText, shouldn't it have been closed?
Comment 11 Tobias Mueller 2010-11-25 18:31:30 UTC
I'd say so.
But does that work as expected now?
Comment 12 Alexandre Franke 2015-06-19 14:37:33 UTC
Matthias, can you confirm is this bug report is still relevant today?
Comment 13 Matthias Clasen 2015-06-23 11:30:54 UTC
My comment from 10 years ago does not say "this works, except for clist and text". But regardless, keeping this bug open for another 10 years is not going to make the world a better place.