Gtk_text_view_scroll_to_iter does not scroll to the desired position

I am trying to make gtk_text_view_scroll_to_iter() to work in my application, but something is wrong in my code, or probably I am facing the part where the documentation says:

Note that this function uses the currently-computed height of the lines in the text buffer. Line heights are computed in an idle handler; so this function may not have the desired effect if it’s called before the height computations.

In the following code the searched text get selected but does not scrolls to it if I call scroll_to_iter() functions, but works fine if I call the scroll_to_mark() function:

    #include <gtk/gtk.h>

const gchar *const buffer =  "This is a long message\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "of course we have another line here";

void scroll_to_iter ( GtkWidget *text_view, GtkTextBuffer *text_buffer, const gchar *const text );
void scroll_to_iter ( GtkWidget *text_view, GtkTextBuffer *text_buffer, const gchar *const text )
{
    GtkTextIter start;
    GtkTextIter match_start;
    GtkTextIter match_end;

    gtk_text_buffer_get_start_iter ( text_buffer, &start );
    gtk_text_buffer_get_end_iter   ( text_buffer, &match_end );

    gboolean flag;
    flag = gtk_text_iter_forward_search ( &start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE, &match_start, &match_end, NULL );

    if ( flag )
    {
        gtk_text_buffer_select_range ( text_buffer, &match_start, &match_end );
        gtk_text_view_scroll_to_iter ( GTK_TEXT_VIEW ( text_view ), &match_start, 0.0, TRUE, 0.0, 0.0 );
    }
}

void scroll_to_mark  ( GtkWidget *text_view, GtkTextBuffer *text_buffer, const gchar *const text );
void scroll_to_mark  ( GtkWidget *text_view, GtkTextBuffer *text_buffer, const gchar *const text )
{
    GtkTextIter start;
    GtkTextIter match_start;
    GtkTextIter match_end;
    GtkTextMark *mark;

    gtk_text_buffer_get_start_iter ( text_buffer, &start );
    gtk_text_buffer_get_end_iter   ( text_buffer, &match_end );

    gboolean flag;
    flag = gtk_text_iter_forward_search ( &start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE, &match_start, &match_end, NULL );

    if ( flag )
    {

        mark = gtk_text_buffer_create_mark ( text_buffer, NULL, &match_end, TRUE );
        gtk_text_buffer_select_range ( text_buffer, &match_start, &match_end );
        gtk_text_view_scroll_to_mark ( GTK_TEXT_VIEW ( text_view ), mark, 0.0, TRUE, 0.0, 0.0 );
        gtk_text_buffer_delete_mark ( text_buffer, mark );
    }
}

int main ( void )
{
    GtkWidget *window;
    GtkWidget *scroll_window;
    GtkWidget *text_view;


    /// ***
    gtk_init ( NULL, NULL );

    /// *** Create a new Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 400, 200 );
    g_signal_connect ( window, "delete-event", gtk_main_quit, NULL );
    g_signal_connect ( window, "destroy",      gtk_main_quit, NULL );

    /// ***
    scroll_window = gtk_scrolled_window_new ( NULL, NULL );
    gtk_container_add ( GTK_CONTAINER ( window ), scroll_window );

    /// ***
    GtkTextBuffer *text_buffer;
    text_buffer = gtk_text_buffer_new ( NULL );
    gtk_text_buffer_set_text ( text_buffer, buffer, -1 );

    /// ***
    text_view = gtk_text_view_new_with_buffer ( text_buffer );
    gtk_container_add ( GTK_CONTAINER ( scroll_window ), text_view );

    /// ***
    scroll_to_iter ( text_view, text_buffer, "another" );

    /// *** 15)
    ///scroll_to_mark ( text_view, text_buffer, "another" );

    /// ***
    gtk_widget_show_all ( window );
    gtk_main ();
}

I was thinking that probably the Function gtk_text_buffer_place_cursor() helps, but drops the selection and the text never gets scrolled to that position.

Thank you

1 Like

You have to use glib_idle_add() to use gtk_text_view_scroll_to_iter() to let Gtk process the new information. I’m not good at C, so I can’t give you the exact code.

Some more information: https://stackoverflow.com/questions/48934458/gtk-sourceview-scroll-to-mark-not-working

1 Like

Well that function takes optional arguments and if the g_idle_add is the C function, then I need some help on use it right, because this one takes only one function and one parameter.

Anyway, I have no idea about how to use it in my case.

With a bit of hacking (I’m sure it is not perfect), I came up with :

#include <gtk/gtk.h>

GtkTextBuffer *text_buffer;
GtkWidget *text_view;

const gchar *const buffer =  "This is a long message\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "And here goes to line two\n"
                             "of course we have another line here";

void scroll_to_iter (const gchar *const text );
void scroll_to_iter (const gchar *const text )
{
    GtkTextIter start;
    GtkTextIter match_start;
    GtkTextIter match_end;

    gtk_text_buffer_get_start_iter ( text_buffer, &start );
    gtk_text_buffer_get_end_iter   ( text_buffer, &match_end );

    gboolean flag;
    flag = gtk_text_iter_forward_search ( &start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE, &match_start, &match_end, NULL );
    g_print("%d flag ", flag);
    if ( flag )
    {
        gtk_text_buffer_select_range ( text_buffer, &match_start, &match_end );
        gtk_text_view_scroll_to_iter ( GTK_TEXT_VIEW ( text_view ), &match_start, 0.0, TRUE, 0.0, 0.0 );
    }
}

void scroll_to_mark  (const gchar *const text );
void scroll_to_mark  (const gchar *const text )
{
    GtkTextIter start;
    GtkTextIter match_start;
    GtkTextIter match_end;
    GtkTextMark *mark;

    gtk_text_buffer_get_start_iter ( text_buffer, &start );
    gtk_text_buffer_get_end_iter   ( text_buffer, &match_end );

    gboolean flag;
    flag = gtk_text_iter_forward_search ( &start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE, &match_start, &match_end, NULL );

    if ( flag )
    {

        mark = gtk_text_buffer_create_mark ( text_buffer, NULL, &match_end, TRUE );
        gtk_text_buffer_select_range ( text_buffer, &match_start, &match_end );
        gtk_text_view_scroll_to_mark ( GTK_TEXT_VIEW ( text_view ), mark, 0.0, TRUE, 0.0, 0.0 );
        gtk_text_buffer_delete_mark ( text_buffer, mark );
    }
}

int main ( void )
{
    GtkWidget *window;
    GtkWidget *scroll_window;


    /// ***
    gtk_init ( NULL, NULL );

    /// *** Create a new Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 400, 200 );
    g_signal_connect ( window, "delete-event", gtk_main_quit, NULL );
    g_signal_connect ( window, "destroy",      gtk_main_quit, NULL );

    /// ***
    scroll_window = gtk_scrolled_window_new ( NULL, NULL );
    gtk_container_add ( GTK_CONTAINER ( window ), scroll_window );

    /// ***
    text_buffer = gtk_text_buffer_new ( NULL );
    gtk_text_buffer_set_text ( text_buffer, buffer, -1 );

    /// ***
    text_view = gtk_text_view_new_with_buffer ( text_buffer );
    gtk_container_add ( GTK_CONTAINER ( scroll_window ), text_view );

    /// ***
    g_idle_add((GSourceFunc )scroll_to_iter, "another" );

    /// *** 15)
    ///scroll_to_mark ( text_view, text_buffer, "another" );

    /// ***
    gtk_widget_show_all ( window );
    gtk_main ();
}

It is not working like that, I get:

error: cast between incompatible function types from ‘void ()(const gchar * const)’ {aka ‘void ()(const char * const)’} to ‘gboolean (*)(void )’ {aka ‘int ()(void *)’} [-Werror=cast-function-type]|

Remove the line

I forgot, sorry about that.

1 Like

This call:

g_idle_add((GSourceFunc )scroll_to_iter, "another" );

should be paired to a function of type GSourceFunc, which is defined as gboolean (* GSourceFunc) (gpointer). You should change the scroll_to_iter declaration to this:

gboolean scroll_to_iter (gpointer data);
gboolean scroll_to_iter (gpointer data)
{
  const char *text = data;

  // ...

  return G_SOURCE_REMOVE;
}

otherwise the results will be undefined.

1 Like

I noticed from the Compiler warning. Thank you both for your time and help.

Is there any available function like this one which lets me pass more than one argument?

at this point to avoid working with global variables I use:

g_object_set_data
g_object_get_data

Like this:

static gboolean scroll_to_iter ( GtkWidget *text_view )
{
    GtkTextIter start;
    GtkTextIter match_start;
    GtkTextIter match_end;

    const gchar *text = NULL;
    GtkTextBuffer *text_buffer;

    text        = g_object_get_data ( G_OBJECT ( text_view ), "text" );
    text_buffer = g_object_get_data ( G_OBJECT ( text_view ), "text_buffer" );

    gtk_text_buffer_get_start_iter ( text_buffer, &start );
    gtk_text_buffer_get_end_iter   ( text_buffer, &match_end );

    gboolean flag;
    flag = gtk_text_iter_forward_search ( &start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE, &match_start, &match_end, NULL );

    if ( flag )
    {
        gtk_text_buffer_select_range ( text_buffer, &match_start, &match_end );
        gtk_text_view_scroll_to_iter ( GTK_TEXT_VIEW ( text_view ), &match_start, 0.0, TRUE, 0.0, 0.0 );
    }

    return G_SOURCE_REMOVE;
}

int main ( void )
{
    GtkWidget *window;
    GtkWidget *scroll_window;
    GtkWidget *text_view;
    GtkTextBuffer *text_buffer;

    /// ***
    gtk_init ( NULL, NULL );

    /// *** Create a new Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 400, 200 );
    g_signal_connect ( window, "delete-event", gtk_main_quit, NULL );
    g_signal_connect ( window, "destroy",      gtk_main_quit, NULL );

    /// ***
    scroll_window = gtk_scrolled_window_new ( NULL, NULL );
    gtk_container_add ( GTK_CONTAINER ( window ), scroll_window );

    /// ***
    text_buffer = gtk_text_buffer_new ( NULL );
    gtk_text_buffer_set_text ( text_buffer, buffer, -1 );

    /// ***
    text_view = gtk_text_view_new_with_buffer ( text_buffer );
    gtk_container_add ( GTK_CONTAINER ( scroll_window ), text_view );

    /// ***
    const gchar *const text = "another";
    g_idle_add ( ( GSourceFunc ) scroll_to_iter, ( gpointer ) text_view );

    g_object_set_data ( G_OBJECT ( text_view ), "text",   (gpointer)text );
    g_object_set_data ( G_OBJECT ( text_view ), "text_buffer", text_buffer );

    /// *** 15)
    ///scroll_to_mark ( text_view, text_buffer, "another" );

    /// ***
    gtk_widget_show_all ( window );
    gtk_main ();
}

Pass a struct pointer.

1 Like

That was also an Option, but I thought that there is a function like which lets me pass those arguments.

Thank you for your time.

C does not have that capability. If you have to pass an aggregate of arguments, you will need to make an aggregate type.

2 Likes

I’ll try to document my self about that.
Thank you.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.